SlideShare a Scribd company logo
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction
to Object-Oriented Programming
with JavaTM
C.Thomas Wu
Naval Postgraduate School
wu23399_fm.qxd 1/10/07 11:53 Page i
A COMPREHENSIVE INTRODUCTION TO OBJECT-ORIENTED PROGRAMMING WITH JAVA
Published by McGraw-Hill, a business unit of The McGraw-Hill Companies, Inc., 1221 Avenue of the Americas,
New York, NY 10020. Copyright © 2008 by The McGraw-Hill Companies, Inc. All rights reserved. No part of this
publication may be reproduced or distributed in any form or by any means, or stored in a database or retrieval system,
without the prior written consent of The McGraw-Hill Companies, Inc., including, but not limited to, in any network
or other electronic storage or transmission, or broadcast for distance learning.
Some ancillaries, including electronic and print components, may not be available to customers outside
the United States.
This book is printed on acid-free paper.
1 2 3 4 5 6 7 8 9 0 DOC/DOC 0 9 8 7
ISBN 978–0–07–352339–2
MHID 0–07–352339–9
Publisher: Alan R. Apt
Executive Marketing Manager: Michael Weitz
Senior Project Manager: Sheila M. Frank
Lead Production Supervisor: Sandy Ludovissy
Associate Media Producer: Christina Nelson
Designer: Rick D. Noel
Cover Designer: Elise Lansdon
(USE) Cover Image: breaking wave on foaming ocean surface, ®Ron Dahlquist/Getty Images
Compositor: ICC Macmillan Inc.
Typeface: 10.5/12 Times Roman
Printer: R. R. Donnelley Crawfordsville, IN
Library of Congress Cataloging-in-Publication Data
Wu, C. Thomas.
A comprehensive introduction to object-oriented programming with Java / C. Thomas
Wu. – 1st ed.
p. cm.
ISBN 978–0–07–352339–2 — ISBN 0–07–352339–9
1. Object-oriented programming (Computer science) 2. Java (Computer program
language) I. Title.
QA76.64.W77 2008
005.117–dc22
2006048064
www.mhhe.com
wu23399_fm.qxd 1/10/07 11:53 Page ii
To my family
wu23399_fm.qxd 1/10/07 11:53 Page iii
wu23399_fm.qxd 1/10/07 11:53 Page iv
v
Preface xiii
Key Differences from the Standard Edition xiii
Book Organization xiv
Hallmark Features of the Text xviii
0 Introduction to Computers and
Programming Languages 1
0.1 A History of Computers 2
0.2 Computer Architecture 4
0.3 Programming Languages 11
0.4 Java 12
1 Introduction to Object-Oriented Programming and
Software Development 15
1.1 Classes and Objects 16
1.2 Messages and Methods 18
1.3 Class and Instance Data Values 20
1.4 Inheritance 23
1.5 Software Engineering and Software
Life Cycle 24
C o n t e n t s
wu23399_fm.qxd 1/10/07 11:53 Page v
2 Getting Started with Java 29
2.1 The First Java Program 30
2.2 Program Components 39
2.3 Edit-Compile-Run Cycle 49
2.4 Sample Java Standard Classes 52
2.5 Sample Development 67
3 Numerical Data 81
3.1 Variables 82
3.2 Arithmetic Expressions 90
3.3 Constants 95
3.4 Displaying Numerical Values 97
3.5 Getting Numerical Input 103
3.6 The Math Class 109
3.7 Random Number Generation 113
3.8 The GregorianCalendar Class 115
3.9 Sample Development 120
3.10 Numerical Representation (Optional) 131
4 Defining Your Own Classes—Part 1 145
4.1 First Example:Defining and Using a Class 146
4.2 Second Example:Defining and Using Multiple Classes 156
4.3 Matching Arguments and Parameters 160
4.4 Passing Objects to a Method 162
4.5 Constructors 167
4.6 Information Hiding and Visibility Modifiers 172
4.7 Class Constants 175
4.8 Local Variables 183
4.9 Calling Methods of the Same Class 185
4.10 Changing Any Class to a Main Class 189
4.11 Sample Development 190
vi Contents
wu23399_fm.qxd 1/10/07 11:53 Page vi
Contents vii
5 Selection Statements 213
5.1 The if Statement 214
5.2 Nested if Statements 225
5.3 Boolean Expressions and Variables 231
5.4 Comparing Objects 239
5.5 The switch Statement 244
5.6 Drawing Graphics 248
5.7 Enumerated Constants 258
5.8 Sample Development 264
6 Repetition Statements 295
6.1 The while Statement 296
6.2 Pitfalls in Writing Repetition Statements 305
6.3 The do–while Statement 311
6.4 Loop-and-a-Half Repetition Control 315
6.5 The for Statement 319
6.6 Nested for Statements 324
6.7 Formatting Output 326
6.8 Loan Tables 331
6.9 Estimating the Execution Time 334
6.10 Recursive Methods (Optional) 338
6.11 Sample Development 343
7 Defining Your Own Classes—Part 2 365
7.1 Returning an Object from a Method 366
7.2 The Reserved Word this 370
7.3 Overloaded Methods and Constructors 378
7.4 Class Variables and Methods 383
wu23399_fm.qxd 1/10/07 11:53 Page vii
viii Contents
7.5 Call-by-Value Parameter Passing 387
7.6 Organizing Classes into a Package 394
7.7 Using Javadoc Comments for Class Documentation 395
7.8 The Complete Fraction Class 400
7.9 Sample Development 410
8 Exceptions and Assertions 437
8.1 Catching Exceptions 438
8.2 Throwing Exceptions and Multiple catch Blocks 445
8.3 Propagating Exceptions 450
8.4 Types of Exceptions 458
8.5 Programmer-Defined Exceptions 461
8.6 Assertions 463
8.7 Sample Development 469
9 Characters and Strings 487
9.1 Characters 488
9.2 Strings 491
9.3 Pattern Matching and Regular Expression 502
9.4 The Pattern and Matcher Classes 509
9.5 Comparing Strings 513
9.6 StringBuffer and StringBuilder 515
9.7 Sample Development 521
10 Arrays and Collections 543
10.1 Array Basics 544
10.2 Arrays of Objects 555
10.3 The For-Each Loop 565
wu23399_fm.qxd 1/10/07 11:53 Page viii
Contents ix
10.4 Passing Arrays to Methods 569
10.5 Two-Dimensional Arrays 576
10.6 Lists and Maps 583
10.7 Sample Development 596
11 Sorting and Searching 619
11.1 Searching 620
11.2 Sorting 624
11.3 Heapsort 632
11.4 Sample Development 645
12 File Input and Output 669
12.1 File and JFileChooser Objects 670
12.2 Low-Level File I/O 679
12.3 High-Level File I/O 684
12.4 Object I/O 693
12.5 Sample Development 700
13 Inheritance and Polymorphism 713
13.1 A Simple Example 714
13.2 Defining Classes with Inheritance 717
13.3 Using Classes Effectively with Polymorphism 721
13.4 Inheritance and Member Accessibility 724
13.5 Inheritance and Constructors 729
13.6 Abstract Superclasses and Abstract Methods 733
13.7 Inheritance versus Interface 738
13.8 Sample Development 739
wu23399_fm.qxd 1/12/07 13:15 Page ix
x Contents
14 GUI and Event-Driven Programming 765
14.1 Simple GUI I/O with JOptionPane 768
14.2 Customizing Frame Windows 771
14.3 GUI Programming Basics 777
14.4 Text-Related GUI Components 787
14.5 Layout Managers 798
14.6 Effective Use of Nested Panels 808
14.7 Other GUI Components 817
14.8 Menus 835
14.9 Handling Mouse Events 839
15 Recursive Algorithms 859
15.1 Basic Elements of Recursion 860
15.2 Directory Listing 861
15.3 Anagram 863
15.4 Towers of Hanoi 866
15.5 Quicksort 868
15.6 When Not to Use Recursion 873
16 Memory Allocation Schemes and
Linked Data Structures 879
16.1 Contiguous Memory Allocation Scheme 881
16.2 Noncontiguous Memory Allocation Scheme 886
16.3 Manipulating Linked Lists 890
16.4 Linked Lists of Objects 903
16.5 Sample Development 908
wu23399_fm.qxd 1/10/07 11:53 Page x
Contents xi
17 Generics and Type Safety 945
17.1 Generic Classes 946
17.2 Generics and Collections 961
17.3 Generics,Inheritance,and Java Interface 969
17.4 Additional Topics and Pitfalls 974
18 List ADT 981
18.1 The List ADT 982
18.2 The List Interface 988
18.3 The Array Implementation of the List ADT 992
18.4 The Linked-List Implementation
of the List ADT 1001
18.5 The Linked Implementation
with the Head Node 1018
18.6 The Iterator Design Pattern 1022
18.7 Sample Development 1027
19 Stack ADT 1035
19.1 The Stack ADT 1036
19.2 The Stack Interface 1040
19.3 The Array Implementation 1042
19.4 The Linked-List Implementation 1047
19.5 Implementation Using NPSList 1052
19.6 Sample Applications:Matching HTML Tags 1053
19.7 Sample Applications:Solving
a Maze with Backtracking 1060
wu23399_fm.qxd 1/10/07 11:53 Page xi
xii Contents
20 Queue ADT 1069
20.1 The Queue ADT 1070
20.2 The Queue Interface 1073
20.3 The Array Implementation 1075
20.4 The Linked-List Implementation 1082
20.5 Implementation Using NPSList 1088
20.6 Priority Queue 1089
Appendix A 1099
Appendix B 1107
Appendix C 1133
Appendix D 1155
Index 1163
wu23399_fm.qxd 1/10/07 11:53 Page xii
xiii
P r e f a c e
This book is an in-depth introduction to object-oriented programming using
the Java programming language. In addition to covering traditional topics for a CS1
course, some of the more advanced topics such as recursion and linked lists are in-
cluded to provide a comprehensive coverage of beginning to intermediate-level ma-
terials. There are more materials in the book than what are normally covered in a
typical CS1 course. An instructor may want to teach some of the chapters on data
structures in an advanced CS1 course. Topics covered in Chapters 16 to 20 are also
suitable for use in a CS2 course.
Key Differences from the Standard Edition
This comprehensive edition is based on An Introduction to Object-Oriented Pro-
gramming with Java, Fourth Edition. The key differences between this comprehen-
sive version and the fourth edition standard version are as follows:
1. Data Structures Chapters. Chapter 16 covers topics on managing linked
nodes. Using this as the foundation, Chapters 18 through 20 present three ab-
stract data types (ADTs) List, Stack, and Queue, respectively. For all three
ADTs, both array-based and linked-list implementations are shown, and their
relative advantages and disadvantages are discussed.
2. More Discussion on Java 5.0 Features. Many of the new Java 5.0 features
are explained and used in the sample programs. They include the enumerator
type, the for-each loop construct, auto boxing and unboxing, and the generics.
One complete chapter (Chapter 17) is dedicated to the generics.
3. Exclusive Use of Console Input and Output. All the GUI related topics,
including the JOptionPane class, are moved to Chapter 14. Sample programs
before Chapter 14 use the standard console input (Scanner) and output
(System.out). Those who want to use JOptionPane for simple input and output
can do so easily by covering Section 14.1 before Chapter 3.
wu23399_fm.qxd 1/10/07 11:53 Page xiii
xiv Preface
Book Organization
There are 21 chapters in this book, numbered from 0 to 20. The first 11 chapters
cover the core topics that provide the fundamentals of programming. Chapters 11 to
15 cover intermediate-level topics such as sorting, searching, recursion, inheritance,
polymorphism, and file I/O. And Chapters 16 to 20 cover topics related to data
structures. There are more than enough topics for one semester. After the first
11 chapters (Ch 0 to Ch 10), instructors can mix and match materials from Chapters 11
to 20 to suit their needs. We first show the dependency relationships among the
chapters and then provide a brief summary of each chapter.
Chapter Dependency
For the most part, chapters should be read in sequence, but some variations are
possible, especially with the optional chapters. Here’s a simplified dependency
graph:
0
1
2
3
4
5
6
7
8 9 10
15
14*
13
12
11
18
19 20
17
16
*Note: Some examples use arrays,
but the use of arrays is not an
integral part of the examples.
These examples can be modified
to those that do not use arrays.
Many topics from the early part
of the chapter can be introduced
as early as after Chapter 2.
wu23399_fm.qxd 1/10/07 11:53 Page xiv
Preface xv
Brief Chapter Summary
Here is a short description of each chapter:
• Chapter 0 is an optional chapter. We provide background information on
computers and programming languages. This chapter can be skipped or as-
signed as an outside reading if you wish to start with object-oriented pro-
gramming concepts.
• Chapter 1 provides a conceptual foundation of object-oriented programming.
We describe the key components of object-oriented programming and illus-
trate each concept with a diagrammatic notation using UML.
• Chapter 2 covers the basics of Java programming and the process of editing,
compiling, and running a program. From the first sample program presented in
this chapter, we emphasize object-orientation. We will introduce the standard
classes String, Date, and SimpleDateFormat so we can reinforce the notion of
object declaration, creation, and usage. Moreover, by using these standard
classes, students can immediately start writing practical programs.We describe
and illustrate console input with System.in and the new Scanner class and output
with System.out.
• Chapter 3 introduces variables, constants, and expressions for manipulating
numerical data. We explain the standard Math class from java.lang and
introduce more standard classes (GregorianCalendar and DecimalFormat) to
continually reinforce the notion of object-orientation. We describe additional
methods of the Scanner class to input numerical values. Random number
generation is introduced in this chapter. The optional section explains how the
numerical values are represented in memory space.
• Chapter 4 teaches the basics of creating programmer-defined classes. We
keep the chapter accessible by introducting only the fundamentals with illus-
trative examples. The key topics covered in this chapter are constructors, vis-
ibility modifiers (public and private), local variables, and passing data to
methods. We provide easy-to-grasp illustrations that capture the essence of
the topics so the students will have a clear understanding of them.
• Chapter 5 explains the selection statements if and switch. We cover boolean
expressions and nested-if statements. We explain how objects are compared
by using equivalence (==) and equality (the equals and compareTo methods).
We use the String and the programmer-defined Fraction classes to make the
distinction between the equivalence and equality clear. Drawing 2-D graphics
is introduced, and a screensaver sample development program is developed.
We describe the new Java 5.0 feature called enumerated type in this chapter.
• Chapter 6 explains the repetition statements while, do–while, and for. Pitfalls
in writing repetition statements are explained. One of the pitfalls to avoid is
the use of float or double for the data type of a counter variable. We illustrate
this pitfall by showing a code that will result in infinite loop. Finding the great-
est common divisor of two integers is used as an example of a nontrivial loop
statement. We show the difference between the straightforward (brute-force)
wu23399_fm.qxd 1/10/07 11:54 Page xv
xvi Preface
and the clever (Euclid’s) solutions. We introduce the Formatter class (new to
Java 5.0) and show how the output can be aligned nicely. The optional last sec-
tion of the chapter introduces recursion as another technique for repetition.
The recursive version of a method that finds the greatest common divisor of
two integers is given.
• Chapter 7 is the second part of creating programmer-defined classes. We
introduce new topics related to the creation of programmer-defined classes
and also repeat some of the topics covered in Chapter 4 in more depth. The
key topics covered in this chapter are method overloading, the reserved
word this, class methods and variables, returning an object from a method,
and pass-by-value parameter passing. As in Chapter 4, we provide many
lucid illustrations to make these topics accessible to beginners. We use the
Fraction class to illustrate many of these topics, such as the use of this and
class methods. The complete definition of the Fraction class is presented in
this chapter.
• Chapter 8 teaches exception handling and assertions. The focus of this chap-
ter is the construction of reliable programs. We provide a detailed coverage of
exception handling in this chapter. We introduce an assertion and show how it
can be used to improve the reliability of finished products by catching logical
errors early in the development.
• Chapter 9 covers nonnumerical data types: characters and strings. Both the
String and StringBuffer classes are explained in the chapter. Another string
class named StringBuilder (new to Java 5.) is briefly explained in this chapter.
An important application of string processing is pattern matching. We describe
pattern matching and regular expression in this chapter. We introduce the
Pattern and Matcher classes and show how they are used in pattern matching.
• Chapter 10 teaches arrays. We cover arrays of primitive data types and of ob-
jects. An array is a reference data type in Java, and we show how arrays are
passed to methods. We describe how to process two-dimensional arrays and
explain that a two-dimensional array is really an array of arrays in Java. Lists
and maps are introduced as a more general and flexible way to maintain a col-
lection of data. The use of ArrayList and HashMap classes from the java.util
package is shown in the sample programs. Also, we show how the WordList
helper class used in Chapter 9 sample development program is implemented
with another map class called TreeMap.
• Chapter 11 presents searching and sorting algorithms. Both N2
and Nlog2N
sorting algorithms are covered. The mathematical analysis of searching and
sorting algorithms can be omitted depending on the students’ background.
• Chapter 12 explains the file I/O. Standard classes such as File and JFile-
Chooser are explained. We cover all types of file I/O, from a low-level byte
I/O to a high-level object I/O. We show how the file I/O techniques are used
to implement the helper classes—Dorm and FileManager—in Chapter 8 and 9
sample development programs. The use of the Scanner class for inputting data
from a textfile is also illustrated in this chapter.
wu23399_fm.qxd 1/10/07 11:54 Page xvi
Preface xvii
• Chapter 13 discusses inheritance and polymorphism and how to use them ef-
fectively in program design. The effect of inheritance for member accessibil-
ity and constructors is explained. We also explain the purpose of abstract
classes and abstract methods.
• Chapter 14 covers GUI and event-driven programming. Only the Swing-
based GUI components are covered in this chapter. We show how to use the
JOptionPane class for a very simple GUI-based input and output. GUI com-
ponents introduced in this chapter include JButton, JLabel, ImageIcon,
JTextField, JTextArea, and menu-related classes. We describe the effective use
of nested panels and layout managers. Handling of mouse events is described
and illustrated in the sample programs. Those who do not teach GUI can skip
this chapter altogether. Those who teach GUI can introduce the beginning part
of the chapter as early as after Chapter 2.
• Chapter 15 covers recursion. Because we want to show the examples where
the use of recursion really shines, we did not include any recursive algorithm
(other than those used for explanation purposes) that really should be written
nonrecursively.
• Chapter 16 covers contiguous and noncontiguous memory allocation schemes
and introduces the concept of linked lists. Ample examples are provided to
illustrate the manipulation of linked lists of primitive data types and linked
lists of objects. This chapter lays the necessary foundation for the students to
learn different techniques for implementing the abstract data types covered in
Chapters 18 through 20.
• Chapter 17 covers new Java 5.0 generics in detail. The chapter describes how
generic classes are defined and how the type safety is supported by generics.
A concrete example of using generics is shown by defining a simple linked list
with generic nodes.
• Chapter 18 introduces the concept of abstract data types (ADT) and covers
the List ADT. Key features of the List ADT are explained and two implemen-
tations using an array and a linked list are shown. The iterator pattern to tra-
verse the elements in the List ADT is introduced.
• Chapter 19 covers the Stack ADT. Key features of the Stack ADT are ex-
plained and two implementations using an array and a linked list are shown.
Sample applications that use stacks are described.
• Chapter 20 covers the Queue ADT. Key features of the Stack ADT are ex-
plained and two implementations using an array and a linked list are shown.
A special type of queue called a priority queue is also intoduced in this
chapter.
wu23399_fm.qxd 1/10/07 11:54 Page xvii
xviii Preface
Development Exercises
give students an opportunity
to practice incremental
development.
Hallmark Features of the Text
Problem Solving
Printing the Initials
Now that we have acquired a basic understanding of Java application programs, let’s
write a new application.We will go through the design,coding,and testing phases of the
software life cycle to illustrate the development process. Since the program we develop
here is very simple,we can write it without really going through the phases.However,it is
extremely important for you to get into a habit of developing a program by following the
software life cycle stages. Small programs can be developed in a haphazard manner, but
not large programs.We will teach you the development process with small programs first,
so you will be ready to use it to create large programs later.
We will develop this program by using an incremental development technique,
which will develop the program in small incremental steps. We start out with a bare-
bones program and gradually build up the program by adding more and more code to
it. At each incremental step, we design, code, and test the program before moving on
to the next step. This methodical development of a program allows us to focus our at-
tention on a single task at each step, and this reduces the chance of introducing errors
into the program.
Problem Statement
We start our development with a problem statement. The problem statement for our
sample programs will be short,ranging from a sentence to a paragraph,but the problem
statement for complex and advanced applications may contain many pages. Here’s the
problem statement for this sample development exercise:
Write an application that asks for the user’s first, middle, and last names and
replies with the user’s initials.
Overall Plan
Our first task is to map out the overall plan for development.We will identify classes nec-
essary for the program and the steps we will follow to implement the program.We begin
with the outline of program logic.For a simple program such as this one,it is kind of obvi-
ous; but to practice the incremental development, let’s put down the outline of program
flow explicitly.We can express the program flow as having three tasks:
1. Get the user’s first,middle,and last names.
2. Extract the initials to formulate the monogram.
3. Output the monogram.
Having identified the three major tasks of the program, we will now identify the
classes we can use to implement the three tasks. First, we need an object to handle the
input. At this point, we have learned about only the Scanner class, so we will use it
here. Second, we need an object to display the result. Again, we will use System.out, as
it is the only one we know at this point for displaying a string value. For the string
Sample Development
2.5 Sample Development
program
tasks
Sample Development Programs
Most chapters include a sample
development section that describes the
process of incremental development.
Development Exercises
For the following exercises, use the incremental development methodology to
implement the program. For each exercise, identify the program tasks, create a
design document with class descriptions, and draw the program diagram. Map
out the development steps at the start. Present any design alternatives and
justify your selection. Be sure to perform adequate testing at the end of each
development step.
8. In the sample development, we developed the user module of the keyless
entry system. For this exercise, implement the administrative module that
allows the system administrator to add and delete Resident objects and
modify information on existing Resident objects. The module will also allow
the user to open a list from a file and save the list to a file. Is it proper to
implement the administrative module by using one class? Wouldn’t it be a
better design if we used multiple classes with each class doing a single,
well-defined task?
9. Write an application that maintains the membership lists of five social clubs
in a dormitory. The five social clubs are the Computer Science Club, Biology
Club, Billiard Club, No Sleep Club, and Wine Tasting Club. Use the Dorm
wu23399_fm.qxd 1/10/07 11:54 Page xviii
Preface xix
Object-Oriented Approach
We take the object-first approach to teaching object-oriented programming with emphasis
on proper object-oriented design.The concept of objects is clearly illustrated from the very
first sample program.
/*
Chapter 2 Sample Program: Displaying a Window
File: Ch2Sample1.java
*/
import javax.swing.*;
class Ch2Sample1 {
public static void main(String[] args) {
JFrame myWindow;
myWindow = new JFrame();
myWindow.setSize(300, 200);
myWindow.setTitle(My First Java Program);
myWindow.setVisible(true);
}
}
Dorm
Door
Resident
User module
Dorm Resident
A helper class
provided to us
A class we
implement
One or more classes
we implement
Administrative
module
Figure 8.8 Program diagrams for the user and administrative modules.Notice the same Dorm and
Resident classes are used in both programs.User and administrative modules will include one or more
classes (at least one is programmer-defined).
Good practices on object-
oriented design are
discussed throughout
the book and illustrated
through numerous
sample programs.
wu23399_fm.qxd 1/10/07 11:55 Page xix
xx Preface
Illustrative Diagrams
Illustrative diagrams are used to explain all key concepts of programming such as the
difference between object declaration and creation,the distinction between the primitive
data type and the reference data type,the call-by-value parameter passing,inheritance,and
many others.
Numerical Data Object
number1 = 237;
number2 = number1;
int number1, number2;
alan = new Professor();
turing = alan;
Professor alan, turing;
number2
number1
turing
alan
number2
number1
turing
alan
number1 = 237;
int number1, number2;
alan = new Professor();
Professor alan, turing;
number2 = number1; turing = alan;
:Professor
:Professor
number2
number1
turing
alan
number1 = 237;
int number1, number2;
alan = new Professor();
Professor alan, turing;
number2 = number1; turing = alan;
237
237
237
Figure 3.3 An effect of assigning the content of one variable to another.
Figure 18.2 Sample version 2 add operations on myList.
Before
After
Before
After
“cat” “gnu” “ape” “dog” “bee”
0 1 2 3 4
“cat”
0
“ape”
1
“dog”
2
“bee”
3
add(1, “gnu”)
throws
index-out-of-bounds-exception
add(5, “gnu”)
myList
“cat”
0
“ape”
1
“dog”
2
“bee”
3
myList
myList
myList
“cat”
0
“ape”
1
“dog”
2
“bee”
3
No structural
change to the list
Lucid diagrams are used effectively to explain
data structures and abstract data types.
wu23399_fm.qxd 1/10/07 11:55 Page xx
Preface xxi
Student Pedagogy
Always define a constructor and initialize data members fully in the
constructor so an object will be created in a valid state.
It is not necessary to create an object for every variable we use. Many novice pro-
grammers often make this mistake.For example,we write
Fraction f1, f2;
f1 = new Fraction(24, 36);
f2 = f1.simplify( );
We didn’t write
Fraction f1, f2;
f1 = new Fraction(24, 36);
f2 = new Fraction(1, 1); //not necessary
f2 = f1.simplify( );
because it is not necessary.The simplify method returns a Fraction object, and in
the calling program, all we need is a name we can use to refer to this returned
Fraction object.Don’t forget that the object name (variable) and the actual object
instance are two separate things.
We can turn our simulation program into a real one by replacing the Door
class with a class that actually controls the door. Java provides a mechanism
called Java Native Interface (JNI) which can be used to embed a link to a low-
level device driver code, so calling the open method actually unlocks the
door.
1. What will be displayed on the console window when the following code is
executed and the user enters abc123 and 14?
Scanner scanner = new Scanner(System.in);
try {
int num1 = scanner.nextInt();
System.out.println(Input 1 accepted);
int num2 = scanner.nextInt();
System.out.println(Input 2 accepted);
} catch (InputMismatchException e) {
System.out.println(Invalid Entry);
}
List the catch blocks in the order of specialized to more general exception classes.
At most one catch block is executed,and all other catch blocks are ignored.
Design Guidelines
provide tips on good
program design.
Things to Remember
boxes provide tips for
students to remember key
concepts.
Tips,Hints,and Pitfalls
provide important points
for which to watch out.
You Might Want to Know
boxes give students
interesting bits of
information.
Quick Check
exercises at the end of
the sections allow
students to test their
comprehension of
topics.
wu23399_fm.qxd 1/10/07 11:55 Page xxi
xxii Preface
Supplements for Instructors and Students
On-Line Learning Center is located at www.mhhe.com/wu
For Instructors
• Complete set of PowerPoints, including lecture notes and figures.
• Complete solutions for the exercises
• Example Bank—Additional examples, which are searchable by topic, are
provided online in a “bank” for instructors.
• Homework Manager/Test Bank—Conceptual review questions are stored in
this electronic question bank and can be assigned as exam questions or home-
work.
• Online labs which accompany this text, can be used in a closed lab, open lab,
or for assigned programming projects.
wu23399_fm.qxd 1/10/07 11:55 Page xxii
Preface xxiii
For Students
• Compiler How Tos provide tutorials on how to get up and running on the
most popular compilers to aid students in using IDEs.
• Interactive Quizzes allow students to test what they learn and get immediate
feedback.
• Source code for all example programs in the book.
• Answers to quick check exercises.
• Glossary of key terms.
• Recent News links relevant to computer science.
• AdditionalTopics such as more on swing and an introduction to data structures.
Acknowledgments
First, I would like to thank the following reviewers for their comments, suggestions,
and encouragement.
Wu Focus Group—Jackson Hole, WY
Elizabeth Adams, James Madison University
GianMario Besana, Depaul University
Michael Buckley, State University of New York, Buffalo
James Cross, Auburn University
Priscilla Dodds, Georgia Perimeter College
Christopher Eliot, University of Massachusetts-Amherst
Joanne Houlahan, John Hopkins University
Len Myers, California Polytechnic State University, San Luis Obispo
Hal Perkins, University of Washington
William Shea, Kansas State University
Marge Skubic, University of Missouri, Columbia
Bill Sverdlik, Eastern Michigan University
Suzanne Westbrook, University of Arizona
wu23399_fm.qxd 1/10/07 11:55 Page xxiii
xxiv Preface
Reviewers
Ajith, Abraham, Oklahoma State University
Elizabeth Adams, James Madison University
David L. Atkins, University of Oregon
GianMario Besana, DePaul University
Robert P. Burton, Brigham Young University
Michael Buckley, State University of New York, Buffalo
Rama Chakrapani, Tennessee Technological University
Teresa Cole, Boise State University
James Cross, Auburn University
Priscilla Dodds, Georgia Perimeter College
Kossi Delali Edoh, Montclair State University
Christopher Eliot, University of Massachusetts-Amherst
Michael Floeser, Rochester Institute of Technology
Joanne Houlahan, John Hopkins University
Michael N. Huhns, University of South Carolina
Eliot Jacobson, University of California, Santa Barbara
Martin Kendall, Montgomery Community College
Mike Litman, Western Illinois University
Len Myers, California Polytechnic State University, San Luis Obispo
Jun Ni, University of Iowa
Robert Noonan, College of William and Mary
Jason S. O’Neal, Mississippi College
Hal Perkins, University of Washington
Gerald Ross, Lane Community College
William Shea, Kansas State University
Jason John Schwarz, North Carolina State University
Marge Skubic, University of Missouri, Columbia
Bill Sverdlik, Eastern Michigan University
Peter Stanchev, Kettering University
Krishnaprasad Thirunarayan, Wright State University
David Vineyard, Kettering University
Suzanne Westbrook, University of Arizona
Melissa Wiggins, Mississippi College
Zhiguang Xu, Valdosta State University.
The following reviewers have provided feedback on the chapters new to this
comprehensive edition:
Eric Matson, Wright State University
Tim Margush, University of Akron
Roxanne Canosa, Rochester Institute of Technology
Ivan Bajic, San Diego State University
Carolyn Miller, North Carolina State
Sunil Prabhakar, Purdue University
Weining Zhang, University of Texas, San Antonio
wu23399_fm.qxd 1/10/07 11:56 Page xxiv
Preface xxv
Personal Story
In September, 2001, I changed my name for personal reasons. Prof C. Thomas
Wu is now Prof Thomas W. Otani. To maintain continuity and not to confuse peo-
ple, we continue to publish the book under my former name. For those who
care to find out a little about my personal history can do so by visiting my web-
site (www.drcaffeine.com).
wu23399_fm.qxd 1/10/07 11:56 Page xxv
wu23399_fm.qxd 1/10/07 11:56 Page xxvi
O b j e c t i v e s
After you have read and studied this chapter,you should be able to
1
0
• State briefly a history of computers.
• Name and describe five major components of
the computer.
• Convert binary numbers to decimal numbers
and vice versa.
• State the difference between the low-level and
high-level programming languages.
Introduction to Computers
and Programming
Languages
wu23392_ch00.qxd 12/12/06 17:23 Page 1
2 Chapter 0 Introduction to Computers and Programming Languages
efore we embark on our study of computer programming, we will present some
background information on computers and programming languages in this optional
chapter. We provide a brief history of computers from the early days to present and
describe the components found in today’s computers. We also present a brief history
of programming languages from low-level machine languages to today’s object-
oriented languages.
0.1 A History of Computers
Humans have evolved from a primitive to a highly advanced society by continually
inventing tools. Stone tools, gunpowder, wheels, and other inventions have changed
the lives of humans dramatically. In recent history, the computer is arguably the
most important invention. In today’s highly advanced society, computers affect our
lives 24 hours a day: class schedules are formulated by computers, student records
are maintained by computers, exams are graded by computers, dorm security sys-
tems are monitored by computers, and numerous other functions that affect us are
controlled by computers.
Although the first true computer was invented in the 1940s, the concept of a
computer is actually more than 160 years old. Charles Babbage is credited with
inventing a precursor to the modern computer. In 1823 he received a grant from
the British government to build a mechanical device he called the Difference
Engine, intended for computing and printing mathematical tables. The device was
based on rotating wheels and was operated by a single crank. Unfortunately, the
technology of the time was not advanced enough to build the device. He ran into
difficulties and eventually abandoned the project.
But an even more grandiose scheme was already with him. In fact, one of the
reasons he gave up on the Difference Engine may have been to work on his new con-
cept for a better machine. He called his new device the Analytical Engine. This
device, too, was never built. His second device also was ahead of its time; the tech-
nology did not yet exist to make the device a reality. Although never built, the Ana-
lytical Engine was a remarkable achievement because its design was essentially
based on the same fundamental principles of the modern computer. One principle
that stands out was its programmability. With the Difference Engine, Babbage would
have been able to compute only mathematical tables, but with the Analytical Engine
he would have been able to compute any calculation by inputting instructions on
punch cards. The method of inputting programs to computers on punch cards was
actually adopted for real machines and was still in wide use as late as the 1970s.
The Analytical Engine was never built, but a demonstration program was
written by Ada Lovelace, a daughter of the poet Lord Byron. The programming lan-
guage Ada was named in honor of Lady Lovelace, the first computer programmer.
In the late 1930s John Atanasoff of Iowa State University, with his graduate
student Clifford Berry, built the prototype of the first automatic electronic calculator.
I n t r o d u c t i o n
B
Charles
Babbage
Difference
Engine
Analytical
Engine
Ada Lovelace
wu23392_ch00.qxd 12/12/06 17:23 Page 2
0.1 A History of Computers 3
One innovation of their machine was the use of binary numbers. (We discuss binary
numbers in Sec. 0.2.)At around the same time, HowardAiken of Harvard University
was working on the Automatic Sequence-Controlled Calculator, known more com-
monly as MARK I, with support from IBM and the U.S. Navy. MARK I was very
similar to the Analytical Engine in design and was described as “Babbage’s dream
come true.”
MARK I was an electromechanical computer based on relays. Mechanical
relays were not fast enough, and MARK I was quickly replaced by machines based
on electronic vacuum tubes. The first completely electronic computer, ENIAC I
(Electronic Numerical Integrator And Calculator), was built at the University of
Pennsylvania under the supervision of John W. Mauchly and J. Presper Eckert.
Their work was influenced by the work of John Atanasoff.
ENIAC I was programmed laboriously by plugging wires into a control
panel that resembled an old telephone switchboard. Programming took an enor-
mous amount of the engineers’ time, and even making a simple change to a pro-
gram was a time-consuming effort. While programming activities were going on,
the expensive computer sat idle. To improve its productivity, John von Neumann
of Princeton University proposed storing programs in the computer’s memory.
This stored program scheme not only improved computation speed but also al-
lowed far more flexible ways of writing programs. For example, because a pro-
gram is stored in the memory, the computer can change the program instructions
to alter the sequence of the execution, thereby making it possible to get different
results from a single program.
We characterized these early computers with vacuum tubes as first-generation
computers. Second-generation computers, with transistors replacing the vacuum
tubes, started appearing in the late 1950s. Improvements in memory devices also
increased processing speed further. In the early 1960s, transistors were replaced by
integrated circuits, and third-generation computers emerged. A single integrated
circuit of this period incorporated hundreds of transistors and made the construction
of minicomputers possible. Minicomputers are small enough to be placed on desk-
tops in individual offices and labs. The early computers, on the other hand, were so
huge that they easily occupied the whole basement of a large building.
Advancement of integrated circuits was phenomenal. Large-scale integrated
circuits, commonly known as computer chips or silicon chips, packed the power
equivalent to thousands of transistors and made the notion of a “computer on a sin-
gle chip” a reality. With large-scale integrated circuits, microcomputers emerged in
the mid-1970s. The machines we call personal computers today are descendants of
the microcomputers of the 1970s. The computer chips used in today’s personal
computers pack the power equivalent to several millions of transistors. Personal
computers are fourth-generation computers.
Early microcomputers were isolated, stand-alone machines. The word per-
sonal describes a machine as a personal device intended to be used by an individual.
However, it did not take long to realize there was a need to share computer resources.
For example, early microcomputers required a dedicated printer. Wouldn’t it make
more sense to have many computers share a single printer? Wouldn’t it also make
sense to share data among computers, instead of duplicating the same data on
MARK I
ENIAC I
stored program
generations of
computers
wu23392_ch00.qxd 12/12/06 17:23 Page 3
individual machines? Wouldn’t it be nice to send electronic messages between the
computers? The notion of networked computers arose to meet these needs.
Computers of all kinds are connected into a network. A network that connects
computers in a single building or in several nearby buildings is called a local-area
network or LAN. A network that connects geographically dispersed computers is
called a wide-area network or WAN. These individual networks can be connected
further to form interconnected networks called internets. The most famous internet
is simply called the Internet. The Internet makes the sharing of worldwide informa-
tion possible and easy. The hottest tool for viewing information on the Internet is a
Web browser. A Web browser allows you to experience multimedia information
consisting of text, audio, video, and other types of information. We will describe
how Java is related to the Internet and Web browsers in Section 0.4.
4 Chapter 0 Introduction to Computers and Programming Languages
network
LAN
WAN
internet
1. Who was the first computer programmer?
2. Who designed the Difference Engine and Analytical Engine?
3. How many generations of computers are there?
0.2 Computer Architecture
A typical computer today has five basic components: RAM, CPU, storage devices,
I/O (input/output) devices, and communication devices. Figure 0.1 illustrates these
five components. Before we describe the components of a computer, we will explain
the binary numbering system used in a computer.
Binary Numbers
To understand the binary number system, let’s first review the decimal number sys-
tem in which we use 10 digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. To represent a number in
the decimal system, we use a sequence of one or more of these digits. The value that
each digit in the sequence represents depends on its position. For example, consider
the numbers 234 and 324. The digit 2 in the first number represents 200, whereas
the digit 2 in the second number represents 20. A position in a sequence has a
value that is an integral power of 10. The following diagram illustrates how the
If you want to learn more about the history of computing,there is a wealth of information
available on the Web.You can start your exploration from
www.yahoo.com/Computers_and_Internet/History
For more information on the pioneers of computers,visit
en.wikipedia.org/wiki/category:Computer_pioneers
wu23392_ch00.qxd 12/12/06 17:23 Page 4
0.2 Computer Architecture 5
values of positions are determined:
The value of a decimal number (represented as a sequence of digits) is the sum
of the digits, multiplied by their position values, as illustrated:
 2  102
 4  101
 8  100
 7  101
 2  100  4  10  8  1  7  110
 200  40  8  710  248.7
2
102
4
101
8
100
• 7
101
104
103
102
101
100
Decimal
Point
Position Values
101
102
• • •
• • •
103
•
Figure 0.1 A simplified view of an architecture for a typical computer.
Output
Devices
Communication
Devices
Input
Devices
RAM
CPU
Storage
Devices
Printer
(output device)
Monitor
(output device)
Main Unit (housing
CPU, RAM, storage
devices, and
communication
devices)
Mouse
(input device)
Keyboard
(input device)
wu23392_ch00.qxd 12/12/06 17:23 Page 5
In the decimal number system, we have 10 symbols, and the position values
are integral powers of 10. We say that 10 is the base or radix of the decimal number
system. The binary number system works the same as the decimal number system
but uses 2 as its base. The binary number system has two digits (0 and 1) called bits,
and position values are integral powers of 2. The following diagram illustrates how
the values of positions are determined in the binary system:
The value of a binary number (represented as a sequence of bits) is the sum of
the bits, multiplied by their position values, as illustrated:
 1  22
 0  21
 1  20
 1  21
 1  4  0  2  1  1  1  12
 4  0  1  12  5.5
So the binary number 101.1 is numerically equivalent to the decimal num-
ber 5.5. This illustration shows how to convert a given binary number to the
decimal equivalent. How about converting a given decimal number to its binary
equivalent?
The following steps show how to convert a decimal number (only the whole
numbers) to the equivalent binary number. The basic idea goes something like
this:
1. Divide the number by 2.
2. The remainder is the bit value of the 20
position.
3. Divide the quotient by 2.
4. The remainder is the bit value of the 21
position.
5. Divide the quotient by 2.
6. The remainder is the bit value of the 22
position.
7. Repeat the procedure until you cannot divide any further, that is, until the
quotient becomes 0.
1
22
0
21
1
20
• 1
21
24 23
• • •
• • •
22 21 20
Binary
Point
Position Values
•
21 22 23
6 Chapter 0 Introduction to Computers and Programming Languages
base-2
numbers
binary number
bits
binary-to-
decimal
conversion
decimal-to-
binary
conversion
wu23392_ch00.qxd 12/12/06 17:23 Page 6
0.2 Computer Architecture 7
When you pay closer attention to the on/off switch on computers and other
electronic devices,you should notice an icon like this
This is a stylized representation of binary digits 0 and 1.
RAM
Random access memory or RAM is a repository for both program instructions and
data manipulated by the program during execution. RAM is divided into cells,
with each cell having a unique address. Typically, each cell consists of 4 bytes (B),
and a single byte (1 B) in turn consists of 8 bits. Each bit, which can be either on
or off, represents a single binary digit. RAM is measured by the number of bytes
it contains. For example, 128 kilobytes (KB) of RAM contains 128  1024 
131,072 B because 1 KB is equal to 210
 1024 B. Notice that 1 K is not equal to
103
, although 103
 1000 is a close approximation to 210
 1024. The first IBM
PC introduced in 1981 came with 16 KB of RAM, and the first Macintosh com-
puter introduced in 1984 came with 128 KB of RAM. In contrast, a typical PC
today has anywhere from 128 MB to 512 MB of RAM. Given that 1 MB is equal
to 1024 KB, we know that 256 MB means 256  1024 KB  262,144 KB 
262,144  1024 B  268,435,456 B.
RAM
byte
The following diagram illustrates the conversion of decimal number 25.
Division Division Division Division Division
#5 #4 #3 #2 #1
24
23
22
21
20
16  8  0  0  1  25
The binary system is more suitable for computers than the decimal system be-
cause it is far easier to design an electrical device that can distinguish two states
(bits 0 and 1) than 10 states (digits 0 through 9). For example, we can represent 1 by
turning the switch on and 0 by turning the switch off. In a real computer, 0 is repre-
sented by electrical voltage below a certain level and 1 by electrical voltage at or
above this level.
12
21
2
5
1

24
1
6
21
1
2
1

12
0
3
21
6
1

6
0
1
21
3
1

2
1
0
21
1
1

0
1
wu23392_ch00.qxd 12/12/06 17:23 Page 7
CPU
The central processing unit or CPU is the brain of a computer. The CPU is the com-
ponent that executes program instructions by fetching an instruction (stored in
RAM), executing it, fetching the next instruction, executing it, and so on until it en-
counters an instruction to stop. The CPU contains a small number of registers, which
are high-speed devices for storing data or instructions temporarily. The CPU also
contains the arithmetic-logic unit (ALU), which performs arithmetic operations such
as addition and subtraction and logical operations such as comparing two numbers.
CPUs are characterized by their clock speeds. For example, in the Intel
Pentium 200, the CPU has a clock speed of 200 megahertz (MHz). The hertz is a
unit of frequency equal to 1 cycle per second. A cycle is a period of time between
two on states or off states. So 200 MHz equals 200,000,000 cycles per second. The
fastest CPU for commercially available personal computers was around 200 MHz
in 1997 when the first edition of this textbook was published. But by the beginning
of 1998, many vendors started selling 300-MHz machines. And in a mere 6 months,
by the middle of 1998, the top-of-the-line personal computers were 400-MHz ma-
chines. As of this writing in late 2002, we see computers with 2.0-GHz (2000-MHz)
CPU being advertised and sold. The increase of the CPU speed in the last two
decades is truly astonishing. The clock speed of the Intel 8080, the CPU introduced
in 1974 that started the PC revolution, was a mere 2 MHz. In contrast, the clock
speed of the Intel Pentium 4 introduced in 2001 was 2 GHz (2000 MHz). Table 0.1
lists some of the Intel processors.
I/O Devices
Input/output or I/O devices allow communication between the user and the CPU.
Input devices such as keyboards and mice are used to enter data, programs, and
commands in the CPU. Output devices such as monitors and printers are used to
display or print information. Other I/O devices include scanners, bar code readers,
magnetic strip readers, digital video cameras, and musical instrument digital inter-
face (MIDI) devices.
Storage Devices
Storage devices such as disk and tape drives are used to store data and programs. Sec-
ondary storage devices are called nonvolatile memory, while RAM is called volatile
memory. Volatile means the data stored in a device will be lost when the power to the
device is turned off. Being nonvolatile and much cheaper than RAM, secondary stor-
age is an ideal medium for permanent storage of large volumes of data. A secondary
storage device cannot replace RAM, though, because secondary storage is far slower
in data access (getting data out and writing data in) compared to RAM.
The most common storage device today for personal computers is a disk
drive. There are two kinds of disks: hard and floppy (also known as diskettes). Hard
disks provide much faster performance and larger capacity, but are normally not re-
movable; that is, a single hard disk is permanently attached to a disk drive. Floppy
disks, on the other hand, are removable, but their performance is far slower and
their capacity far smaller than those of hard disks. As the standard floppy disks can
8 Chapter 0 Introduction to Computers and Programming Languages
CPU
register
clock speed
I/O devices
nonvolatile and
volatile
memory
wu23392_ch00.qxd 12/12/06 17:23 Page 8
store only up to approximately 1.44 MB, they are becoming less useful in today’s
world of multimegabyte image and sound files. They are fast becoming obsolete,
and hardly anybody uses them anymore. Removable storage media with much
higher capacity such as zip disks (capable of holding 100 to 250 MB of data) re-
placed floppy disks in late 1990s. Computer technology moves so quickly that zip
disks themselves are already becoming obsolete. The most common form of
portable storage medium today (2006) is a compact USB flash drive, whose capac-
ity ranges from 125 MB to 16 GB.
Hard disks can store a huge amount of data, typically ranging from 20 GB
(gigabyte; 1 GB  1024 MB) to 80 GB for a standard desktop PC in 2002. Portable
and removable hard disk drives, with performance and capacity that rival those of
nonremovable hard disks, are also available, but their use is not widespread.
Compact disks (CDs) are very popular today for storing massive amounts of
data, approximately 700 MB. Many software packages we buy today—computer
0.2 Computer Architecture 9
Table
Table 0.1
A table of Intel processors.For some CPUs,several types with different
clock speeds are possible.In such case,only the fastest clock speed is
shown.For more information on Intel CPUs,visit http://www.intel.com.
Date Clock Speed
CPU Introduced (MHz)
4004 11/15/71 0.108
8008 4/1/72 0.200
1970s 8080 4/1/74 2
8088 6/1/79 8
80286 2/1/82 12
1980s 80386SX 6/16/88 16
80486DX 4/10/89 25
Pentium 3/22/93 66
Pentium Pro 11/1/95 200
1990s Pentium II 5/7/97 300
Pentium II Xeon 6/29/98 400
Pentium III 10/25/99 733
Xeon 9/25/01 2000
2000s
Pentium 4 4/27/01 2000
Itanium 2 7/8/02 1000
Pentium 4 Extreme 2/2/04 3400
Edition
Core 2 Extreme 7/27/06 3200
wu23392_ch00.qxd 12/12/06 17:23 Page 9
games, word processors, and others—come with a single CD. Before the CD became
apopularstoragedeviceforcomputers,somesoftwarecamewithmorethan20floppy
diskettes. Because of the massive storage capacity of the CD, most computer vendors
eliminated printed manuals altogether by putting the manuals on the CD.
10 Chapter 0 Introduction to Computers and Programming Languages
Communication Devices
A communication device connects the personal computer to an internet. The most
common communication device for computers at home and in small offices is the
modem. A modem, which stands for modulator-demodulator, is a device that con-
verts analog signals to digital and digital signals to analog. By using a modem, a
computer can send to and receive data from another computer over the phone line.
The most critical characteristic of a modem is its transmission speed, which is mea-
sured in bits per second (bps). A typical speed for a modem is 56,000 bps, com-
monly called a 56K modem. Under an ideal condition (no line noise or congestion),
a 56K modem can transfer a 1 MB file in about 21
⁄2 minutes. Frequently, though, the
actual transfer rate is much lower than the possible maximum. So-called DSL and
cable modems are not truly modems because they transfer data strictly in digital
mode, which allows for much faster connection speeds of 144K or above. High-
speed satellite connection to the Internet is also available today.
A communication device for connecting a computer to a LAN is a network
interface card (NIC). A NIC can transfer data at a much faster rate than the fastest
modem. For instance, a type of NIC called 10BaseT can transfer data at the rate
of 10 Mbps over the network. Traditional networks are connected, or wired, by the
cables. Increasingly, networks are connected wirelessly, where data are carried over
radio waves. Wireless networking is called WiFi or 802.11 networking. Today you
will find wireless networking almost universally available at airports and hotels.
communication
device
1. Name five major components of a computer.
2. What is the difference between volatile and nonvolatile memory?
3. What does the acronym CPU stand for?
4. How many bytes does the 64 KB RAM have?
5. Which device connects a computer to the Internet using a phone line?
Today we see more and more companies are even eliminating CDs and promoting
“boxless”online distribution of software.With this scheme,we go to their websites
and download the software,after paying for it with our credit card.Maybe someday
we may be able to buy textbooks in the same manner and stop carrying 20 lb of
dead trees in our backpacks.
wu23392_ch00.qxd 12/12/06 17:23 Page 10
0.3 Programming Languages
Programming languages are broadly classified into three levels: machine languages,
assembly languages, and high-level languages. Machine language is the only pro-
gramming language the CPU understands. Each type of CPU has its own machine
language. For example, the Intel Pentium and Motorola PowerPC understand differ-
ent machine languages. Machine-language instructions are binary-coded and very
low level—one machine instruction may transfer the contents of one memory loca-
tion into a CPU register or add numbers in two registers. Thus we must provide many
machine-language instructions to accomplish a simple task such as finding the aver-
age of 20 numbers.Aprogram written in machine language might look like this:
10110011 00011001
01111010 11010001 10010100
10011111 00011001
01011100 11010001 10010000
10111011 11010001 10010110
One level above machine language is assembly language, which allows
“higher-level” symbolic programming. Instead of writing programs as a sequence
of bits, assembly language allows programmers to write programs by using sym-
bolic operation codes. For example, instead of 10110011, we use MV to move the
contents of a memory cell into a register. We also can use symbolic, or mnemonic,
names for registers and memory cells. A program written in assembly language
might look like this:
MV 0, SUM
MV NUM, AC
ADD SUM, AC
STO SUM, TOT
Since programs written in assembly language are not recognized by the CPU,
we use an assembler to translate programs written in assembly language into
machine-language equivalents. Compared to writing programs in machine lan-
guage, writing programs in assembly language is much faster, but not fast enough
for writing complex programs.
High-level languages were developed to enable programmers to write pro-
grams faster than when using assembly languages. For example, FORTRAN
(FORmula TRANslator), a programming language intended for mathematical com-
putation, allows programmers to express numerical equations directly as
X = (Y + Z) / 2
COBOL (COmmon Business-Oriented Language) is a programming language in-
tended for business data processing applications. FORTRAN and COBOL were de-
veloped in the late 1950s and early 1960s and are still in use. BASIC (Beginners
All-purpose Symbolic Instructional Code) was developed specifically as an easy
language for students to learn and use. BASIC was the first high-level language
0.3 Programming Languages 11
machine
language
machine code
assembly code
assembler
high-level code
high-level
languages
assembly
language
wu23392_ch00.qxd 12/12/06 17:23 Page 11
available for microcomputers. Another famous high-level language is Pascal, which
was designed as an academic language. Since programs written in a high-level lan-
guage are not recognized by the CPU, we must use a compiler to translate them to
assembly language equivalents.
The programming language C was developed in the early 1970s at ATT Bell
Labs. The C++ programming language was developed as a successor of C in the
early 1980s to add support for object-oriented programming. Object-oriented pro-
gramming is a style of programming gaining wider acceptance today. Although the
concept of object-oriented programming is old (the first object-oriented program-
ming language, Simula, was developed in the late 1960s), its significance wasn’t
realized until the early 1980s. Smalltalk, developed at Xerox PARC, is another
well-known object-oriented programming language. The programming language
we use in this book is Java, the newest object-oriented programming language,
developed at Sun Microsystems.
0.4 Java
Java isanewobject-orientedlanguagethatisreceivingwideattentionfrombothindus-
try and academia. Java was developed by James Gosling and his team at Sun Microsys-
tems in California. The language was based on C and C++ and was originally intended
for writing programs that control consumer appliances such as toasters, microwave
ovens,andothers.ThelanguagewasfirstcalledOak,namedaftertheoaktreeoutsideof
Gosling’s office, but the name was already taken, so the team renamed it Java.
Java is often described as a Web programming language because of its use in
writing programs called applets that run within a Web browser. That is, you need a
Web browser to execute Java applets. Applets allow more dynamic and flexible dis-
semination of information on the Internet, and this feature alone makes Java an at-
tractive language to learn. However, we are not limited to writing applets in Java.
We can write Java applications also. A Java application is a complete stand-alone
program that does not require a Web browser. A Java application is analogous to a
program we write in other programming languages. In this book, we focus on Java
applications because our objective is to teach the fundamentals of object-oriented
programming that are applicable to all object-oriented programming languages.
We chose Java for this textbook mainly for its clean design. The language de-
signers of Java took a minimalist approach; they included only features that are in-
dispensable and eliminated features that they considered excessive or redundant.
This minimalist approach makes Java a much easier language to learn than other
object-oriented programming languages. Java is an ideal vehicle for teaching the
fundamentals of object-oriented programming.
12 Chapter 0 Introduction to Computers and Programming Languages
compiler
Java
applet
application
• Charles Babbage invented the Difference Engine and Analytical Engine,
precursors to the modern computer.
• Ada Lovelace is considered the first computer programmer.
• The first two modern computers were MARK I and ENIAC I.
S u m m a r y
wu23392_ch00.qxd 12/12/06 17:23 Page 12
• John von Neumann invented the stored-program approach of executing
programs.
• Computers are connected into a network. Interconnected networks are
called internets.
• Binary numbers are used in computers.
• A typical computer consists of five components: RAM, CPU, storage
devices, I/O devices, and communication devices.
• There are three levels of programming languages: machine, assembly, and
high-level.
• Java is one of the newest high-level programming languages in use today.
This textbook teaches how to program using Java.
Exercises 13
K e y C o n c e p t s
network
LAN
WAN
internets and Internet
CPU
RAM
I/O devices
communication devices
binary numbers
binary-to-decimal conversion
machine language
assembly language
assembler
high-level language
compiler
Java
E x e r c i s e s
1. Visit your school’s computer lab or a computer store, and identify the
different components of the computers you see. Do you notice any unique
input or output devices?
2. Visit your school’s computer lab and find out the CPU speed, RAM size, and
hard disk capacity of its computers.
3. Convert these binary numbers to decimal numbers.
a. 1010
b. 110011
c. 110.01
d. 111111
4. Convert these decimal numbers to binary numbers.
a. 35
b. 125
c. 567
d. 98
wu23392_ch00.qxd 12/12/06 17:23 Page 13
5. What is the maximum decimal number you can represent in 4 bits? 16 bits?
N bits?
6. If a computer has 128 MB of RAM, how many bytes are there?
7. How do high-level programming languages differ from low-level
programming languages?
8. Consider a hypothetical programming language called Kona. Using Kona,
you can write a program to compute and print out the sum of 20 integers
entered by the user:
let sum = 0;
repeat 20 times [
let X = next input;
add X to sum;
]
printout sum;
Is Kona a high-level language? Why or why not?
14 Chapter 0 Introduction to Computers and Programming Languages
wu23392_ch00.qxd 12/12/06 17:23 Page 14
Introduction to Object-Oriented
Programming and Software
Development
O b j e c t i v e s
After you have read and studied this chapter,you should be able to
• Name the basic components of object-
oriented programming.
• Differentiate classes and objects.
• Differentiate class and instance
methods.
• Differentiate class and instance data
values.
• Draw program diagrams using icons for
classes,objects,and other components of
object-oriented programming.
• Describe the significance of inheritance in
object-oriented programs.
• Name and explain the stages of the software
life cycle.
15
1
wu23399_ch01.qxd 12/12/06 17:24 Page 15
efore we begin to write actual programs, we need to introduce a few basic concepts
of object-oriented programming (OOP), the style of programming we teach in this
book. The purpose of this chapter is to give you a feel for object-oriented program-
ming and to introduce a conceptual foundation of object-oriented programming.You
may want to refer to this chapter as you progress through the book. What we discuss
in the next four sections is independent of any particular programming language.
16 Chapter 1 Introduction to Object-Oriented Programming and Software Development
I n t r o d u c t i o n
B
object-
oriented
programming
object
Those of you who have some experience in programming, whether object-
oriented or non-object-oriented,will probably find many similarities between Java
and the programming languages you already know.This similarity may accelerate
your learning process, but in many cases what seems to be similar at first may
turn out to be quite different. So please do not jump to any conclusions about
similarity prematurely.
Another purpose of this chapter is to introduce the software development
process. To be able to write programs, knowledge of the components of object-
oriented programs is not enough. We must learn the process of developing pro-
grams. We will present a brief introduction to the software development process in
this chapter.
1.1 Classes and Objects
The two most important concepts in object-oriented programming are the class
and the object. In the broadest term, an object is a thing, both tangible and intangi-
ble, that we can imagine. A program written in object-oriented style will consist
of interacting objects. For a program to keep track of student residents of a college
dormitory, we may have many Student, Room, and Floor objects. For another pro-
gram to keep track of customers and inventory for a bicycle shop, we may have
Customer, Bicycle, and many other types of objects. An object is comprised of data
and operations that manipulate these data. For example, a Student object may con-
sist of data such as name, gender, birth date, home address, phone number, and age
and operations for assigning and changing these data values. We will use the nota-
tion shown in Figure 1.1 throughout the book to represent an object. The notation
we used in the book is based on the industry standard notation called UML, which
stands for Unified Modeling Language. In some of the illustrations, we relax the
rules of UML slightly for pedagogy.
Almost all nontrivial programs will have many objects of the same type.
For example, in the bicycle shop program we expect to see many Bicycle and other
objects. Figure 1.2 shows two Bicycle objects with the names Moto-1 and Moto-2
and one Customer object with the name Jon Java.
wu23399_ch01.qxd 12/12/06 17:24 Page 16
Inside a program we write instructions to create objects. For the computer to
be able to create an object, we must provide a definition, called a class. A class is a
kind of mold or template that dictates what objects can and cannot do. An object
is called an instance of a class. An object is an instance of exactly one class. An
instance of a class belongs to the class. The two Bicycle objects Moto-1 and Moto-2
are instances of the Bicycle class. Once a class is defined, we can create as many
instances of the class as a program requires.
1.1 Classes and Objects 17
class
instance
Object name
We use a rectangle to
represent an object and
place the underlined
name of the object
inside the rectangle.
Example: account1
This is an object
named account1.
Figure 1.1 A graphical representation of an object.
Jon Java : Customer
Moto-2 : Bicycle
Moto-1 : Bicycle
An object name is followed
by the class name.
Figure 1.2 Two Bicycle objects with the names Moto-1 and Moto-2 and one Customer object with the name
Jon Java.
A class must be defined before you can create an instance (object) of the class.
Figure 1.3 shows a diagram that we will use throughout the book to represent
a class.
1. Draw an object diagram for a Person class and two Person objects, Ms. Latte
and Mr. Espresso.
2. What must be defined before you can create an object?
wu23399_ch01.qxd 12/12/06 17:24 Page 17
1.2 Messages and Methods
In writing object-oriented programs we must first define classes, and while the pro-
gram is running, we use the classes and objects from these classes to accomplish
tasks. A task can range from adding two numbers, to computing an interest payment
for a college loan, to calculating the reentry angle of a space shuttle. To instruct a
class or an object to perform a task, we send a message to it. For example, we send
a message deposit to an Account object to deposit $100.
For a class or an object to process the message, it must be programmed ac-
cordingly. You cannot just send a message to any class or object. You can send a
message only to the classes and objects that understand the message you send. For
a class or an object to process the message it receives, it must possess a matching
method, which is a sequence of instructions that a class or an object follows to
perform a task. A method defined for a class is called a class method, and a method
defined for an object is an instance method.
18 Chapter 1 Introduction to Object-Oriented Programming and Software Development
Example:
We use a rectangle to
represent a class with
its name appearing
inside the rectangle.
Class Name
Notice the name of a
class is not underlined
while the name of an
object is.
Account
Figure 1.3 A graphical representation of a class.
Many beginning programmers may not see the distinction between the class and object as
clearly as the more experienced programmers do.It may be helpful to compare the class
and object to a woodcut and the prints produced from the woodcut.A woodcut is a block
of wood engraved with a design for printing.Once you have a woodcut,you can make as
many prints as you wish.Similarly,once you have a class,you can make as many objects
from the class.Also,just as you cannot make prints without having a woodcut,you cannot
create an object without first defining a class.For sample prints by the 19th-century
Japanese artist Hiroshige,visit
http://www.ibiblio.org/wm/paint/auth/hiroshige/
Another helpful analogy is a robot factory.A factory is a class,and the robots
produced from the factory are the objects of the class.To create robots (instance),we
need the factory (class) first.Those interested in mobile robots can visit
http://www.ai.mit.edu/projects/mobile-robots/robots.html
message
method
class and
instance
methods
wu23399_ch01.qxd 12/12/06 17:24 Page 18
Let’s look at an example of an instance method first. Suppose a method called
walk is defined for a Robot object and instructs the robot to walk a designated dis-
tance. With this method defined, we can send the message walk to a Robot object,
along with the distance to be walked. A value we pass to an object is called an
argument of a message. Notice that the name of the message we send to an object or
a class must be the same as the method’s name. In Figure 1.4 we represent the send-
ing of a message.
The diagram in Figure 1.4 illustrates one-way communication; that is, an
object carries out the requested operation (it walks the designated distance) but
does not respond to the message sender. In many situations we need a reply in
which an object responds by returning a value to the message sender. For exam-
ple, suppose we want to know the distance from a robot to its nearest obstacle.
The designer of a robot may include a method getObstacleDistance that returns
the desired value. The diagram in Figure 1.5 shows a method that returns a value
to the message sender. Instead of returning a numerical value, a method can re-
port back the status of the requested operation. For example, a method walk can
be defined to return the status success/fail to indicate whether the specified
distance was covered successfully or not (e.g., it fails when the robot bumps into
an obstacle).
Now let’s look at an example of class methods. The class method getMaxi-
mumSpeed shown in Figure 1.6 returns the maximum possible speed of all Robot
objects. A method such as getMaximumSpeed that deals with collective information
about the instances of a class is usually defined as a class method. So we define an
instance method for a task that pertains to an individual instance and a class method
for a task that pertains to all instances.
1.2 Messages and Methods 19
argument
walk(25)
fido : Robot
Message walk with
the argument 25.
Figure 1.4 Sending the message walk to a Robot object.
getObstacleDistance( )
distance
fido : Robot
This shows that we are not
sending any argument.
This shows the value
distance is returned as
a response to the message.
Figure 1.5 The result distance is returned to the sender of the message.
wu23399_ch01.qxd 12/12/06 17:24 Page 19
20 Chapter 1 Introduction to Object-Oriented Programming and Software Development
getMaximumSpeed( )
maximum speed
Robot
Figure 1.6 The maximum possible speed of all Robot objects is returned by the class method
getMaximumSpeed.
1. Draw an object diagram of an Account object with instance methods deposit
and withdraw.
2. Is the getObstacleDistance method an instance or a class method?
1.3 Class and Instance Data Values
Suppose the method deposit of an Account object instructs the object to add a given
amount to the current balance. Where does the object keep the current balance?
Remember that an object is comprised of data values and methods. Analogous to
defining class and instance methods, we can define class and instance data values.
For example, we define an instance data value current balance for Account objects
to record the current balance. Figure 1.7 shows three Account objects with their
data values current balance. Notice that they all have the same data value current
balance.All instances of the same class will possess the same set of data values. The
actual dollar amounts for current balance, as the diagram illustrates, differ from one
instance to another. Items such as opening balance and account number are other
possible instance data values for Account objects.
A class data value is used to represent information shared by all instances or to
represent collective information about the instances. For example, if every account
must maintain a minimum balance of, say, $100, we can define a class data value
minimum balance. An instance can access the class data values of the class to which
it belongs, so every Account object can access the class data value minimum balance.
Jill’s : Account
1304.98
current balance
Jack’s : Account
354.00
current balance
John’s : Account
908.55
current balance
Figure 1.7 Three Account objects possess the same data value current balance, but the actual dollar
amounts differ.
instance data
value
class data value
wu23399_ch01.qxd 12/12/06 17:24 Page 20
Figure 1.8 shows how we represent a class data value. Notice that we underline the
class data value. Because the objects of a class are underlined, and the class data val-
ues are accessible to all objects of the class, we likewise underline the class data
value to show this relationship. Data values are also called data members because
they belong to a class or instance of the class.
To appreciate the significance of a class data value, let’s see what happens if
we represent minimum balance as an instance data value. Figure 1.9 shows three
Account objects having different dollar amounts for the current balance but the
same dollar amount for the minimum balance. Obviously, this duplication of mini-
mum balance is redundant and wastes space. Consider, for example, what happens
if the bank raises the minimum balance to $200. If there are 100 Account objects,
then all 100 copies of minimum balance must be updated. We can avoid this by
defining minimum balance as a class data value. Figure 1.10 shows another exam-
ple where the opening and closing times are shared by all cafeterias on campus.
There are two types of data values: those that can change over time and those
that cannot. A data value that can change is called a variable, and one that cannot
1.3 Class and Instance Data Values 21
John’s : Account
908.55
current balance
Jill’s : Account
1304.98
current balance
Account
minimum balance
100.00
Jack’s : Account
354.00
current balance
Notice the class data value is
underlined to show the fact that this
value is accessible to individual
objects, which are underlined.
Figure1.8 Three Account objects sharing information (minimumbalance  $100) stored as a class data value.
data member
variable
John’s : Account
908.55
current balance
minimum balance
100.00
Jill’s : Account
1304.98
current balance
minimum balance
100.00
Jack’s : Account
354.00
current balance
minimum balance
100.00
Figure1.9 ThreeAccountobjectsduplicatinginformation(minimumbalance  $100)ininstancedatavalues.
wu23399_ch01.qxd 12/12/06 17:24 Page 21
change is a constant. Figure 1.11 illustrates how we represent and distinguish be-
tween variables and constants. We use the keyword frozen for constants to indicate
that they cannot change. Notice that we now have four kinds of data values: class
variables, class constants, instance variables, and instance constants.
22 Chapter 1 Introduction to Object-Oriented Programming and Software Development
Union : Cafeteria
1917.34
revenue
West : Cafeteria
2306.99
revenue
QuikBite : Cafeteria
430.75
revenue
Cafeteria
opens
0600
closes
2100
Figure 1.10 Three Cafeteria objects sharing the same opening and closing times,stored as class data values.
John’s : Account
908.55
current balance
opening balance
{frozen}
246.00
Jill’s : Account
1304.98
current balance
opening balance
{frozen}
50.00
Account
100.00
minimum balance
account prefix
{frozen}
6427
Jack’s : Account
354.00
current balance
opening balance
{frozen}
100.00
We assume this number is
a prefix to the account
number of all accounts, and
the prefix never changes.
This keyword indicates
the value is locked and
cannot be changed.
Figure 1.11 Graphical representations for four types of data values:class variable,class constant,instance
variable,and instance constant.
constant
wu23399_ch01.qxd 12/12/06 17:24 Page 22
1.4 Inheritance
When we used the Account class and its instances to illustrate object-oriented con-
cepts, some of you were probably thinking about checking accounts, while others
may have been thinking about savings accounts. We did not distinguish between the
two in the examples. But when we look at the problem a little more carefully, we
will realize that in fact these two types of accounts are different, even though they
share many features.
In general, using only a single class to model two or more entities that are
similar but different is not good design. In object-oriented programming, we use a
mechanism called inheritance to design two or more entities that are different but
share many common features. First we define a class that contains the common fea-
tures of the entities. Then we define classes as an extension of the common class
inheriting everything from the common class. We call the common class the
superclass and all classes that inherit from it subclasses. We also call the superclass
an ancestor and the subclass a descendant. Other names for superclass and subclass
are base class and derived class, respectively. For the bank example, we can define
a superclass Account and then define Savings and Checking as subclasses of
Account. We represent the superclass and its subclasses as shown in Figure 1.12.
Notice that we draw arrows from each subclass to its superclass because a subclass
can refer to items defined in its superclass, but not vice versa.
Inheritance is not limited to one level. A subclass can be a superclass of other
classes, forming an inheritance hierarchy. Consider the example shown in
Figure 1.13. Inheritance is very powerful, and if it is used properly, we can develop
1.4 Inheritance 23
1. What is the difference between a constant and a variable?
2. Draw an object diagram of a Person object with the three instance variables
name, age, and gender.
Account
Savings Checking
Figure 1.12 A superclass Account and its subclasses Savings and Checking.
inheritance
superclass and
subclass
wu23399_ch01.qxd 12/12/06 17:24 Page 23
complex programs very efficiently and elegantly. The flip side of using a very pow-
erful tool is that if we do not use it correctly, we could end up in a far worse situation
than if we did not use it. We will be seeing many examples of inheritance through-
out this book. In Chapter 2, for example, we will introduce many classes that come
with the Java system. Most of these classes are defined using inheritance. We will
provide an in-depth discussion of inheritance and related topics in Chapter 13.
24 Chapter 1 Introduction to Object-Oriented Programming and Software Development
Student
Law
Doctoral
Masters
Graduate Undergraduate
Commuting Resident
Figure 1.13 An example of inheritance hierarchy among different types of students.
1. If Class A inherits from Class B, which is a superclass? Which is a subclass?
2. Draw a diagram that shows Class A is inheriting from Class B.
3. What are the other names for superclass and subclass?
4. If we have Animal, Insect, and Mammal classes, which one will be a
superclass?
5. Model different types of vehicles, using inheritance. Include Vehicle,
Automobile, Motorcycle, Sports Car, Sedan, and Bicycle.
1.5 Software Engineering and Software Life Cycle
When we say computer programming, we are referring not only to writing Java
commands, but also to a whole process of software development. Knowing a pro-
gramming language alone is not enough to become a proficient software developer.
wu23399_ch01.qxd 12/12/06 17:24 Page 24
You must know how to design a program. This book will teach you how to design
programs in an object-oriented manner.
We construct a house in well-defined stages and apply the engineering princi-
ples in all stages. Similarly, we build a program in stages and apply disciplined
methodology in all stages of program development. The sequence of stages from
conception to operation of a program is called the software life cycle, and software
engineering is the application of a systematic and disciplined approach to the
development, testing, and maintenance of a program.
There are five major phases in the software life cycle: analysis, design, coding,
testing, and operation. Software starts its life from the needs of a customer. A person
wants an online address book, for example. In the analysis phase, we perform a fea-
sibility study. We analyze the problem and determine whether a solution is possible.
Provided that a solution is possible, the result of this phase is a requirements speci-
fication that describes the features of a program. The features must be stated in a
manner that is testable. One of the features for the address book program may be the
capability to search for a person by giving his or her first name. We can test this
feature by running the program and actually searching for a person. We verify that
the program behaves as specified when the first name of a person in the address
book and the first name of a person not in the address book are entered as a search
condition. We do this testing in the testing phase, which we will explain shortly.
In the design phase, we turn a requirements specification into a detailed design
of the program. For an object-oriented design, the output from this phase will be a
set of classes that fulfill the requirements. For the address book program, we may
design classes such as Person, Phone, and others.
In the coding phase, we implement the design into an actual program, in our
case, a Java program. Once we have a well-constructed design, implementing it into
actual code is really not that difficult. The difficult part is the creation of the design,
and in this book, we place greater emphasis on the design aspect of the software
construction.
When the implementation is completed, we move to the testing phase. In this
phase, we run the program, using different sets of data to verify that the program
runs according to the specification. Two types of testing are possible for object-
oriented programs: unit testing and integration testing. With unit testing, we test
classes individually. With integration testing, we test that the classes work together
correctly. Activity to eliminate programming error is called debugging. An error
could be a result of faulty implementation or design. When there’s an error, we need
to backtrack to earlier phases to eliminate the error.
Finally, after the testing is successfully concluded, we enter the operation
phase, in which the program will be put into actual use. The most important and
time-consuming activity during the operation phase is software maintenance. After
the software is put into use, we almost always have to make changes to it. For
example, the customer may request additional features, or previously undetected er-
rors may be found. Software maintenance means making changes to software. It is
estimated that close to 70 percent of the cost of software is related to software main-
tenance. So naturally, when we develop software, we should aim for software that
is easy to maintain. We must not develop a piece of software hastily to reduce the
1.5 Software Engineering and Software Life Cycle 25
software life
cycle
software
engineering
analysis
design
coding
testing
operation
software
maintenance
debugging
wu23399_ch01.qxd 12/12/06 17:24 Page 25
software development cost. We should take time and care to design and code soft-
ware correctly even if it takes longer and costs more to develop initially. In the long
run, carefully crafted software will have a lower total cost because of the reduced
maintenance cost. Here’s an important point to remember:
26 Chapter 1 Introduction to Object-Oriented Programming and Software Development
Well-designed and -constructed software is easy to maintain.
In this book, we will focus on the design, coding, and testing phases. We will pre-
sent a requirements specification in the form of a problem statement for the sample
programs we will develop in this book. We present the first sample program devel-
oped by following the design, coding, and testing phases in Chapter 2. We will
come back to the discussion of software engineering and the software life cycle
throughout the book and provide more details.
1. Name the stages of the software life cycle.
2. How does the quality of design affect the software maintenance cost?
3. What is debugging?
• The style of programming we teach in this book is called object-oriented
programming.
• An object is an instance of a class. Many instances can be created from a
single class.
• There are class and instance methods. We can send messages to objects and
classes if they possess matching methods.
• There are class and instance data values. Data values are also called data
members.
• Inheritance is a powerful mechanism to model two or more entities that are
different but share common features.
• The sequence of software development stages from conception to operation
is called the software life cycle.
S u m m a r y
wu23399_ch01.qxd 12/12/06 17:24 Page 26
• Five major phases of the software life cycle are analysis, design, coding,
testing, and operation.
• Software engineering is the application of a systematic and disciplined
approach to the development, testing, and maintenance of a program.
Exercises 27
K e y C o n c e p t s
object-oriented programming
class
object
message
class and instance methods
instance and class data values
variable
constant
inheritance
superclass (ancestor, base class)
subclass (descendant, derived class)
software life cycle
software engineering
analysis
design
coding
testing
operation
E x e r c i s e s
1. Graphically represent a Vehicle class and three Vehicle objects named car1,
car2, and car3.
2. Graphically represent a Person class with the following components:
• Instance variables name, age, and gender.
• Instance methods setName, getName, and getAge.
• Class method getAverageAge.
3. Design a CD class where a CD object represents a single music CD. What
kinds of information (artist, genre, total playing time, etc.) do you want to
know about a CD? Among the information in which you are interested,
which are instance variables? Are there any class variables or class constants?
4. Suppose the Vehicle class in Exercise 1 is used in a program that keeps track
of vehicle registration for the Department of Motor Vehicles. What kinds of
instance variables would you define for such Vehicle objects? Can you think
of any useful class variables for the Vehicle class?
5. Suppose the following formulas are used to compute the annual vehicle
registration fee for the vehicle registration program of Exercise 4:
• For cars, the annual fee is 2 percent of the value of the car.
• For trucks, the annual fee is 5 percent of the loading capacity (in pounds)
of the truck.
Define two new classes Car and Truck as subclasses of Vehicle. Hint:
Associate class and instance variables common to both Car and Truck
to Vehicle.
wu23399_ch01.qxd 12/12/06 17:24 Page 27
6. Consider a student registration program used by the registrar’s office. The
program keeps track of students who are registered for a given semester. For
each student registered, the program maintains the student’s name, address,
and phone number; the number of classes in which the student is enrolled;
and the student’s total credit hours. The program also keeps track of the total
number of registered students. Define instance and class variables of a
Student class that is suitable for this program.
7. Suppose the minimum number and maximum number of courses for which a
student can register are different depending on whether the student is a
graduate, undergraduate, or work/study student. Redo Exercise 6 by defining
classes for different types of students. Relate the classes, using inheritance.
8. Imagine you are given the task of designing an airline reservation system that
keeps track of flights for a commuter airline. List the classes you think would be
necessary for designing such a system. Describe the data values and methods
you would associate with each class you identify. Note: For this exercise and
Exercises 9 through 12, we are not expecting you to design the system in
complete detail. The objective of these exercises is to give you a taste of
thinking about a program at a very high level. Try to identify about a half dozen
or so classes, and for each class, describe several methods and data members.
9. Repeat Exercise 8, designing a university course scheduling system. The
system keeps track of classes offered in a given quarter, the number of
sections offered, and the number of students enrolled in each section.
10. Repeat Exercise 8, designing the state Department of Motor Vehicles
registration system. The system keeps track of all licensed vehicles and
drivers. How would you design objects representing different types of
vehicles (e.g., motorcycles and trucks) and drivers (e.g., class A for
commercial licenses and class B for towing vehicles)?
11. Repeat Exercise 8, designing a sales tracking system for a fast-food
restaurant. The system keeps track of all menu items offered by the
restaurant and the number of daily sales per menu item.
12. When you write a term paper, you have to consult many references: books,
journal articles, newspaper articles, and so forth. Repeat Exercise 8,
designing a bibliography organizer that keeps track of all references you
used in writing a term paper.
13. Consider the inheritance hierarchy given in Figure 1.12. List the features
common to all classes and the features unique to individual classes. Propose
a new inheritance hierarchy based on the types of accounts your bank offers.
14. Consider a program that maintains an address book. Design an inheritance
hierarchy for the classes such as Person, ProfessionalContact, Friend, and
Student that can be used in implementing such a program.
15. Do you think the design phase is more important than the coding phase?
Why or why not?
16. How does the quality of design affect the total cost of developing and
maintaining software?
28 Chapter 1 Introduction to Object-Oriented Programming and Software Development
wu23399_ch01.qxd 12/12/06 17:24 Page 28
Getting Started with Java
O b j e c t i v e s
After you have read and studied this chapter,you should be able to
• Identify the basic components of Java
programs.
• Write simple Java programs.
• Describe the difference between object
declaration and object creation.
• Describe the process of creating and running
Java programs.
• Use the Date,SimpleDateFormat,String,and
Scanner classes from the standard Java
packages.
• Develop Java programs,using the incremental
development approach.
29
2
wu23399_ch02.qxd 12/12/06 17:26 Page 29
e will describe the basic structure of simple Java programs in this chapter. We
will also describe the steps you follow to run Java programs. We expect you to
actually run these sample programs to verify that your computer (either your own or
the one at the school’s computer center) is set up properly to run the sample
programs presented in the book. It is important to verify this now. Otherwise, if you
encounter a problem later, you won’t be able to determine whether the problem is
the result of a bad program or a bad setup. Please check Appendix A for information
on how to run the textbook’s sample programs.
We will develop a sample application program in Section 2.4 following the
design, coding, and testing phases of the software life cycle. We stress here again
that our objective in this book is to teach object-oriented programming and how to
apply object-oriented thinking in program development. The Java language is
merely a means to implement a design into an executable program. We chose Java
for this book because Java is a much easier language than other object-oriented pro-
gramming languages to use to translate a design into an actual code. Beginning stu-
dents often get lost in the language details and forget the main objective of learning
the development process, but the use of Java should minimize this problem.
2.1 The First Java Program
Our first Java application program displays a window on the screen, as shown in Fig-
ure 2.1. The size of the window is set to 300 pixels wide and 200 pixels high. A pixel
is a shorthand for picture element, and it is the standard unit of measurement for the
screen resolution.Acommon resolution for a 17-in screen, for example, is 1024 pixels
wide and 768 pixels high. The title of the window is set to My First Java Program.
30 Chapter 2 Getting Started with Java
I n t r o d u c t i o n
W
pixel
Figure 2.1 Result of running the Ch2Sample1 program.The window size is 300 by 200 pixels and has the
title My First Java Program.
wu23399_ch02.qxd 12/12/06 17:26 Page 30
Although this program is very simple, it still illustrates the fundamental structure of an
object-oriented program, which is as follows:
2.1 The First Java Program 31
An object-oriented program uses objects.
It may sound too obvious, but let’s begin our study of object-oriented programming
with this obvious notion. Here’s the program code:
/*
Chapter 2 Sample Program: Displaying a Window
File: Ch2Sample1.java
*/
import javax.swing.*;
class Ch2Sample1 {
public static void main(String[] args) {
JFrame myWindow;
myWindow = new JFrame();
myWindow.setSize(300, 200);
myWindow.setTitle(My First Java Program);
myWindow.setVisible(true);
}
}
This will not concern the majority of you, but if you are using a Java development
tool that does not let you stop a running program easily, then insert the statement
myWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
after
myWindow.setVisible(true);
so the program terminates automatically when the frame window is closed.Please
read Appendix A for more information.
wu23399_ch02.qxd 12/12/06 17:26 Page 31
This program declares one class called Ch2Sample1, and the class includes
one method called main. From this main method, the Ch2Sample1 class creates and
uses a JFrame object named myWindow by sending the three messages setSize,
setTitle, and setVisible to the object. The JFrame class is one of many classes that
come with the Java system. An instance of this JFrame class is used to represent a
single window on the computer screen. To differentiate the classes that program-
mers define, including ourselves, and the predefined classes that come with the Java
system, we will call the first programmer-defined classes and the latter Java stan-
dard classes, or simply, standard classes. We also use the term system classes to
refer to the standard classes.
Expressing this program visually results in the program diagram shown in
Figure 2.2. In this diagram, we draw individual messages, but doing so would eas-
ily clutter a diagram when we have more than a handful of messages. Instead of
drawing messages individually, we can draw one arrow to represent a dependency
relationship. For this program, we say the Ch2Sample1 class is dependent on the
services provided by a JFrame object, because the Ch2Sample1 class sends mes-
sages to the MyWindow object. We draw a dotted arrow from Ch2Sample1 to
myWindow to indicate the dependency relationship, as shown in Figure 2.3
We begin the explanation of the program from the following core five lines
of code:
JFrame myWindow;
myWindow = new JFrame();
myWindow.setSize(300, 200);
myWindow.setTitle(My First Java Program);
myWindow.setVisible(true);
32 Chapter 2 Getting Started with Java
programmer-
defined classes
standard
classes
program
diagram
dependency
relationship
myWindow : JFrame
Ch2Sample1
setTitle(“My First Java Program”)
setSize(300, 200)
setVisible(true)
Figure 2.2 The program diagram for the Ch2Sample1 program.
Ch2Sample1
myWindow : JFrame
Figure 2.3 The program diagram for the Ch2Sample1 program that shows the dependency relationship.
wu23399_ch02.qxd 12/12/06 17:26 Page 32
We will explain the rest of the program in Section 2.2. These five lines of code
represent the crux of the program, namely, an object-oriented program that uses
objects. The rule to remember in using objects is as follows:
2.1 The First Java Program 33
object
declaration
syntax
identifier
To use an object in a program,first we declare and create an object,and then we
send messages to it.
In the remainder of this section, we will describe how to declare an object,
create an object, and use an object by sending messages to the object.
Object Declaration
Every object we use in a program must be declared. An object declaration desig-
nates the name of an object and the class to which the object belongs. Its syntax is
class name object names ;
where object names is a sequence of object names separated by commas and
class name is the name of a class to which these objects belong. Here’s how the
general syntax is matched to the object declaration of the program:
Class Name Object Names
The class must be One object is
defined beforehand. declared here.
JFrame myWindow;
Here are more examples:
Account checking;
Customer john, jack, jill;
The first declaration declares an Account object named checking, and the second
declaration declares three Customer objects.
To declare an object as an instance of some class, the class must be defined
already. First we will study how to use objects from system classes. Later in the
book, we will show you how to define your own classes, from which you can create
instances.
When we declare an object, we must give it a name. Any valid identifier that
is not reserved for other uses can be used as an object name. A Java identifier is a se-
quence of letters, digits, underscores (_), and dollar signs ($) with the first one being
wu23399_ch02.qxd 12/12/06 17:26 Page 33
a letter. We use an identifier to name a class, object, method, and others. The
following words are all valid identifiers:
MyFirstApplication
FunTime
ComputeArea
DEFAULT_VALUE
Upper- and lowercase letters are distinguished, so the following four identi-
fiers are distinct:
myWindow mywindow
MYwindow MYWINDOW
No spaces are allowed in an identifier, and therefore, the three lines
Sample Program
My First Application
Program FunTime
are all invalid identifiers.
Since upper- and lowercase letters are distinguished, you can use robot as the
name for an object of the class Robot. We name objects in this manner whenever
possible in this book so we can easily tell to which class the object belongs. We fol-
low the Java standard naming convention of using an uppercase letter for the first
letter of the class names and a lowercase letter for the first letter of the object names
in this book. It is important to follow the standard naming convention so others who
read your program can easily distinguish the purposes of identifiers. Programs that
follow the standard naming convention are easier to read than those that do not. And
remember that software maintenance is easier with easy-to-understand programs.
When an identifier consists of multiple words, the Java naming convention
dictates the first letter from every word, except the first word, will be capitalized, for
example, myMainWindow, not mymainwindow.
34 Chapter 2 Getting Started with Java
standard
naming
convention
Follow the standard naming convention in writing your Java programs to
make them easier to read.
Table 2.2 in the Summary section summarizes the naming convention.
Object Creation
No objects are actually created by the declaration. An object declaration simply
declares the name (identifier) that we use to refer to an object. For example, the
declaration
JFrame myWindow;
wu23399_ch02.qxd 12/12/06 17:26 Page 34
designates that the name myWindow is used to refer to a JFrame object, but the
actual JFrame object is not yet created. We create an object by invoking the new
operator. The syntax for new is
object name = new class name ( arguments ) ;
where object name is the name of a declared object, class name is the name of
the class to which the object belongs, and arguments is a sequence of values
passed to the new operation. Let’s match the syntax to the actual statement in the
sample program:
Object Name Class Name
Name of the object An instance of this
we are creating class is created.
myWindow = new JFrame ( ) ;
Argument
No arguments
are used here.
Figure 2.4 shows the distinction between object declaration and creation.
Figure 2.5 shows the relationship between the UML-based program diagram and
the state-of-memory diagram. The state-of-memory diagram borrows the notation
from UML for consistency, but it is not a true UML diagram because it uses sym-
bols and notations not found in UML.
Now, consider the following object declaration and two statements of object
creation:
Customer customer;
customer = new Customer( );
customer = new Customer( );
What do you think will happen? An error? No. It is permissible to use the same
name to refer to different objects of the same class at different times. Figure 2.6
2.1 The First Java Program 35
new operator
object creation
syntax
Instead of writing statements for object declaration and creation separately, we
can combine them into one statement. We can write,for example,
Student john = new Student();
instead of
Student john;
john = new Student();
wu23399_ch02.qxd 12/12/06 17:26 Page 35
shows the state-of-memory diagram after the second new is executed. Since there
is no reference to the first Customer object anymore, eventually it will be erased
and returned to the system. Remember that when an object is created, a certain
amount of memory space is allocated for storing this object. If this allocated but
unused space is not returned to the system for other uses, the space gets wasted. This
36 Chapter 2 Getting Started with Java
State-of-Memory
Notation
Program Diagram
Notation
account : Account
account
:Account
The state-of-memory diagram uses the
same UML notation, but it also includes
symbols and notations not found in UML.
Figure 2.5 Relationship between the state-of-memory diagram and the program diagram notation.
State of Memory
Account account;
account = new Account( );
A Account account;
B account = new Account( );
after is executed
A
account
after is executed
B
The identifier account is
declared and space is
allocated in memory.
An Account object is created
and the identifier account is
set to refer to it.
account
:Account
Figure 2.4 Distinction between object declaration and object creation.
wu23399_ch02.qxd 12/12/06 17:26 Page 36
returning of space to the system is called deallocation, and the mechanism to deal-
locate unused space is called garbage collection.
Message Sending
After the object is created, we can start sending messages to it. The syntax for send-
ing a message to an object is
object name . method name ( arguments ) ;
where object name is an object name, method name is the name of a method
of the object, and arguments is a sequence of values passed to the method. In
the sample program, we send the setVisible message with the argument true to
the mainWindow object to make it appear on the screen. Once again, let’s match the
components in the general syntax to the actual statement:
Object Name Method Name
Name of the object to which The name of the message
we are sending a message we are sending
myWindow . setVisible ( true ) ;
Argument
The argument we are passing
with the message
Figure 2.7 shows the correspondence between message sending as repre-
sented in the program diagram and in the Java statement. Because the object that
receives a message must possess a corresponding method, we often substitute the
expression sending a message with calling a method. We will use these expressions
interchangeably.
2.1 The First Java Program 37
customer
:Customer :Customer
The first Customer object will be
deallocated eventually because
there are no references to it anymore.
Created with the
second new.
Created with the
first new.
Customer customer;
customer = new Customer();
customer = new Customer();
Figure 2.6 The state after two new commands are executed.
garbage
collection
message-
sending syntax
wu23399_ch02.qxd 12/12/06 17:26 Page 37
Notice the argument for the setVisible message does not include double quotes
as did the one for the setTitle message in the example shown on page 32. The argu-
ment true is one of the two possible logical values (the other is false) used in Java
programs. We will study more about the use of logical values later in the book, start-
ing from Chapter 5. For now, it suffices to remember that there are two logical
values— true and false—used for certain specific purposes.
Passing true in the setVisible message makes the receiving object appear on
the screen. Passing false makes the object disappear from the screen. So, for exam-
ple, if we write
myWindow.setVisible(true);
myWindow.setVisible(false);
myWindow.setVisible(true);
then myWindow will appear once, disappear, and then appear on the screen again.
(Note: Because the computer will execute these statements so quickly, you may not
notice any difference from the original program. See Exercise 22 on page 77.)
The word true (and false) is called a reserved word. It is an identifier that is
used for a specific purpose and cannot be used for any other purpose, such as for the
name of an object.
38 Chapter 2 Getting Started with Java
myWindow:JFrame
setVisible(true)
Program
Diagram
Corresponding
Java Statement
Note: We can place method
icons on either side of a
class or instance icon.
myWindow . setVisible ( true ) ;
Figure 2.7 Correspondence between message sending as represented in the program diagram and in the
actual Java statement.
The expression calling object O’s method M is synonymous with sending message
M to object O.
reserved word
1. Which of the following are invalid identifiers?
a. one
b. my Window
c. 1234
wu23399_ch02.qxd 12/12/06 17:26 Page 38
d. DecafeLattePlease
e. hello
f. JAVA
g. hello, there
h. acct122
2. What’s wrong with the following code?
JFrame myWindow();
myWindow.setVisible(true);
3. Is there anything wrong with the following declarations?
mainWindow MainWindow;
Account, Customer account, customer;
4. Which of the following statements is valid?
a. myFirstWindow.setVisible( true );
b. myFirstWindow.setVisible( true );
2.2 Program Components
Now that we have covered the crux of the first sample program, let’s examine the
rest of the program. The first sample application program Ch2Sample1 is composed
of three parts: comment, import statement, and class declaration. These three parts
are included universally in Java programs.
2.2 Program Components 39
comments
A Java program is composed of comments, import statements,and class
declarations.
You can write a Java program that includes only a single class declaration, but
that is not the norm. In any nontrivial program, you will see these three components.
We explain the three components and their subparts in this section.
Comments
In addition to the instructions for computers to follow, programs contain comments
in which we state the purpose of the program, explain the meaning of code, and pro-
vide any other descriptions to help programmers understand the program. Here’s
wu23399_ch02.qxd 12/12/06 17:26 Page 39
the comment in the sample Ch2Sample1 program:
40 Chapter 2 Getting Started with Java
/*
Chapter 2 Sample Program: Displaying a Window
File: Ch2Sample1.java
*/
import javax.swing.*;
class Ch2Sample1 {
public static void main(String[] args) {
JFrame myWindow;
myWindow = new JFrame();
myWindow.setSize(300, 200);
myWindow.setTitle(My First Java Program);
myWindow.setVisible(true);
}
}
Comment
A comment is any sequence of text that begins with the marker /* and termi-
nates with another marker */. The beginning and ending comment markers are
matched in pairs; that is, every beginning marker must have a matching ending
marker. A beginning marker is matched with the next ending marker that appears.
Any beginning markers that appear between the beginning marker and its matching
ending marker are treated as part of the comment. In other words, you cannot put a
comment inside another comment. The examples in Figure 2.8 illustrate how the
matching is done.
Another marker for a comment is double slashes //. This marker is used for a
single-line comment marker. Any text between the double-slash marker and the end
of a line is a comment. The following example shows the difference between mul-
tiline and single-line comments:
/*
This is a comment with
three lines of
text.
*/
// This is a comment
// This is another comment
// This is a third comment
comment
markers
single-line
comment
marker
wu23399_ch02.qxd 12/12/06 17:26 Page 40
The third type of comment is called a javadoc comment. It is a specialized
comment that can appear before the class declaration and other program elements
yet to be described in the book. We will explain more about javadoc comments in
Chapter 7.
Comments are intended for the programmers only and are ignored by the
computer. Therefore, comments are really not necessary in making a program exe-
cutable, but they are an important aspect of documenting the program. It is not
enough to write a program that executes correctly. We need to document the pro-
gram, and commenting the program is an important part of program documentation.
Other parts of program documentation include program diagrams, programmers’
work logs, design documents, and user manuals. If you can write a program once
and use it forever without ever modifying it, then writing a program with no com-
ments may be tolerable. However, in the real world, using programs without ever
making any changes almost never happens. For example, you may decide to add
new features and capabilities or modify the way the user interacts with the program.
Even if you don’t improve the program, you still have to modify the program when
you detect some errors in it. Also, for commercial programs, those who change the
programs are most often not the ones who developed them. When the time comes
2.2 Program Components 41
javadoc
comment
*/
/* This is a comment on one line */
/*
Comment number 1
*/
/*
Comment number 2
*/
/*
/*
/*
This is a comment
*/
.
An error: no matching
beginning marker
These two markers are
part of the comment.
Figure 2.8 How the beginning and ending comment markers are matched.
Although not required to run the program,comments are indispensable in writing
easy-to-understand code.
wu23399_ch02.qxd 12/12/06 17:26 Page 41
for a programmer to modify his own or someone else’s program, the programmer
must first understand the program, and program documentation is an indispensable
aid to understanding the program.
There are several different uses of comments. The first is the header comment.
At the beginning of a program, we place a comment to describe the program. We
characterize such a comment as a header comment. We also may include header
comments at the beginning of methods to describe their purposes. Depending on the
length and complexity of programs, the description may range from short and sim-
ple to long and very detailed. A typical header comment for a beginning program-
ming class may look something like this:
42 Chapter 2 Getting Started with Java
/*
* Program: TextEditor
*
* Author: Decafe Latte
* decafe@latte.com
*
* Written: May 1, 2006
*
* Course: Comp Sci 101
* Spring 2006
* Program Assignment No. 7
*
* Compiler: JDK 1.5
* Platform: Windows XP
*
* Description:
* This is a simple text editor. The editor allows the user
* to save text to a file and read text from a file. The
* editor displays text using Courier font only and does not
* allow formatting (e.g., bold, italic, etc.). The editor
* supports standard editing functions Cut, Copy, and
* Paste, but does not support Undo. For more details,
* please refer to the TxEditReadme file.
*/
header
comment
typical header
comment for a
beginning
programming
class
Note: The use of the
asterisks is in the style of
javadoc,but this is not a
javadoc comment.
For your own programs, you should write header comments following the
guideline provided by your instructor. For listing the sample programs in the book,
we will include only the program name and a short description in the header com-
ment, mainly for reference purposes. The header comment in the actual programs,
available from our website, includes additional information.
wu23399_ch02.qxd 12/12/06 17:26 Page 42
Another use of comments is to explain code whose purpose may not be obvi-
ous. Your aim is always to write easily understandable, self-explanatory program
code. But at times this is not possible, and you should attach comment to code that
is not so easy to understand. There also are times when the original code may not
work as intended, and as a temporary measure, you modify the code slightly so the
program will continue to work. You should clearly mark such modification with a
comment, so you remember what you have done. If you did not put in an appropri-
ate comment and later read your code without remembering about the modification,
you would have no idea why you wrote such code. If you cannot understand your
own code, imagine the frustration of other programmers (or your T.A. or instructor)
trying to understand your modified code.
Yet another use of comments is to identify or summarize a block of code. Sup-
pose a program is divided into three major parts: getting input values from the user,
performing computation by using those values, and displaying the computation re-
sults. You can place comments at the top of each part to delineate the three major
parts clearly.
Remember that adding comments to a poorly designed program will not make
it a better program. Your foremost goal is to develop a well-designed program that
runs efficiently and is easy to understand. Commenting a program is only a means
toward that goal, not a goal itself. In fact, excessive use of comments makes it
harder to follow and understand a program.
2.2 Program Components 43
package
Always aim for self-explanatory code.Do not attempt to make poorly written
code easier to read by comments.Good comments are not a substitute for
good code.Bad code is bad,no matter how well your comments are written.
Comment markers are useful in disabling a portion of a program.Let’s say you find
a portion that may be causing the program to crash, and you want to try out
different code for the problem portion. Instead of replacing the whole problem
portion with new code, you can leave the questionable code in the program by
converting it into a “comment” with comment markers. You can remove the
comment markers if you need this code later.
Import Statement
We develop object-oriented programs by using predefined classes, both system-
and programmer-defined, whenever possible and defining our own classes when
no suitable predefined classes are available. In Java, classes are grouped into
packages, and the Java system comes with numerous packages. We also can logi-
cally group our own classes into a package so they can be reused conveniently by
other programs.
wu23399_ch02.qxd 12/12/06 17:26 Page 43
To use a class from a package, we refer to the class in our program by using
the following format:
package name . class name
For example, to use the Resident class in the dorm package, we refer to it as
dorm.Resident
which we read as “dorm dot Resident.” This notation is called dot notation.
A package can include subpackages, forming a hierarchy of packages. In re-
ferring to a class in a deeply nested package, we use multiple dots. For example, we
write
javax.swing.JFrame
to refer to the class JFrame in the javax.swing package; that is, the swing package
is inside the javax package. Dot notation with the names of all packages to which
a class belongs is called the class’s fully qualified name. Using the fully qualified
name of a class is frequently too cumbersome, especially when we have to refer
to the same class many times in a program. We can use the import statement to
avoid this problem. Here’s the original Ch2Sample1 program that uses the import
statement:
44 Chapter 2 Getting Started with Java
dot notation
fully qualified
name
/*
Chapter 2 Sample Program: Displaying a Window
File: Ch2Sample1.java
*/
import javax.swing.*;
class Ch2Sample1 {
public static void main(String[] args) {
JFrame myWindow;
myWindow = new JFrame();
myWindow.setSize(300, 200);
myWindow.setTitle(My First Java Program);
myWindow.setVisible(true);
}
}
Import Statement
The import statement allows the program to
refer to classes defined in the designated pack-
age without using the fully qualified class name.
wu23399_ch02.qxd 12/12/06 17:26 Page 44
And here’s the same Ch2Sample1 program without the import statement:
2.2 Program Components 45
/*
Chapter 2 Sample Program: Displaying a Window
File: Ch2Sample1.java
*/
class Ch2Sample1 {
public static void main(String[] args) {
javax.swing.JFrame myWindow;
myWindow = new javax.swing.JFrame ();
myWindow.setSize(300, 200);
myWindow.setTitle(My First Java Program);
myWindow.setVisible(true);
}
}
No import statement
Fully qualified names
Instead of using the expression javax.swing.JFrame to refer to the class, we
can refer to it simply as
JFrame
by including the import statement
import javax.swing.JFrame;
at the beginning of the program. Notice that the import statement is terminated by a
semicolon. If we need to import more than one class from the same package, then
instead of using an import statement for every class, we can import them all by
using asterisk notation:
import package name . * ;
For example, if we state
import javax.swing.*;
wu23399_ch02.qxd 12/12/06 17:26 Page 45
then we are importing all classes from the javax.swing package. We use this asterisk
notation in our sample program, even when we use only one of the many classes
available in the javax.swing package. We could have used
import javax.swing.JFrame;
but it is more conventional to use asterisk notation. Notice that the package names
are all in lowercase letters. This is another standard Java naming convention.
Chapter 4 includes greater discussion of packages.
46 Chapter 2 Getting Started with Java
When we say “import a package,” it sounds as if we are copying all those classes
into our programs. That is not the case. Importing a package is only a shorthand
notation for referencing classes.The only effect of importing a package is the elim-
ination of the requirement to use the fully qualified name.No classes are physically
copied into our programs.
Class Declaration
A Java program is composed of one or more classes; some are predefined classes,
while others are defined by us. In the first sample program, there are two classes—
JFrame and Ch2Sample1. The JFrame class is one of the standard classes, and the
Ch2Sample1 class is the class we define ourselves. To define a new class, we must
declare it in the program, or make a class declaration. The syntax for declaring the
class is
class class name {
class member declarations
}
where class name is the name of the class and class member declarations is a
sequence of class member declarations. The word class is a reserved word used to
mark the beginning of a class declaration. A class member is either a data value or
a method. We can use any valid identifier that is not reserved to name the class.
Here’s the class declaration in the sample Ch2Sample1 program:
class
declaration
/*
Chapter 2 Sample Program: Displaying a Window
File: Ch2Sample1.java
*/
import javax.swing.*;
wu23399_ch02.qxd 12/12/06 17:26 Page 46
One of the classes in a program must be designated as the main class. The
main class of the sample program is Ch2Sample1. Exactly how you designate a class
as the main class of the program depends on which Java program development tool
you use. We will use the name of a main class to refer to a whole application. For
example, we say the Ch2Sample1 class when we refer to the class itself, and we say
the Ch2Sample1 application when we refer to the whole application.
If we designate a class as the main class, then we must define a method called
main, because when a Java program is executed, the main method of a main class is
executed first. To define a method, we must declare it in a class.
Method Declaration
The syntax for method declaration is
modifiers return type method name ( parameters ) {
method body
}
where modifiers is a sequence of terms designating different kinds of methods,
return type is the type of data value returned by a method, method name is the
name of a method, parameters is a sequence of values passed to a method, and
method body is a sequence of instructions. Here’s the method declaration for the
main method:
2.2 Program Components 47
main class
method
declaration
class Ch2Sample1 {
public static void main(String[] args) {
JFrame myWindow;
myWindow = new JFrame();
myWindow.setSize(300, 200);
myWindow.setTitle(My First Java Program);
myWindow.setVisible(true);
}
}
Class Declaration
Every program
must include at
least one class.
/*
Chapter 2 Sample Program: Displaying a Window
File: Ch2Sample1.java
*/
wu23399_ch02.qxd 12/12/06 17:26 Page 47
48 Chapter 2 Getting Started with Java
import javax.swing.*;
class Ch2Sample1 {
public static void main(String[] args) {
JFrame myWindow;
myWindow = new JFrame();
myWindow.setSize(300, 200);
myWindow.setTitle(My First Java Program);
myWindow.setVisible(true);
}
}
Method Declaration
This declaration declares
the main method.
Let’s match these components to the actual method declaration of the sample
program:
We do not explain the meanings of modifiers, return types, and parameters
here. We will explain them in detail gradually as we progress through the book. For
now, we ask you to follow a program template that we present next.
A Program Template for Simple Java Applications
The diagram in Figure 2.9 shows a program template for simple Java applications.
You can follow this program template to write very simple Java applications. The
structure of the sample program Ch2Sample1 follows this template.
Method Body
Consists of a sequence
of instructions
Parameter
Modifier
Modifier Method Name
Return Type
JFrame myWindow;
myWindow = new JFrame();
myWindow.setSize(300, 200);
myWindow.setTitle(My First Java Program);
myWindow.setVisible(true);
public
}
static void main ( String[] args ) {
wu23399_ch02.qxd 12/12/06 17:26 Page 48
2.3 Edit-Compile-Run Cycle
We will walk through the steps involved in executing the first sample program.
What we outline here are the overall steps in the edit-compile-run cycle common to
any Java development tool you use. You need to get detailed instructions on how to
use your chosen development tool to actually run programs. The steps we present in
this section should serve as a guideline for more detailed instructions specific to
your program development tool. Additional information on how to run Java pro-
grams can be found in Appendix A.
Step 1
Type in the program, using an editor, and save the program to a file. Use the name
of the main class and the suffix .java for the filename. This file, in which the pro-
gram is in a human-readable form, is called a source file.
2.3 Edit-Compile-Run Cycle 49
Comment
Use a comment to
describe the program.
Import Statements
Include a sequence of
import statements.
public static void main(String[] args) {
}
}
class {
Class Name
Give a descriptive
name to the main class.
Method Body
Include a sequence
of instructions.
Figure 2.9 A program template for simple Java applications.
edit-compile-
run cycle
source file
/*
Chapter 2 Sample Program: Displaying a Window
File: Ch2Sample1.java
*/
Ch2Sample1.java
wu23399_ch02.qxd 12/12/06 17:26 Page 49
Step 2
Compile the source file. Many compilers require you to create a project file and then
place the source file in the project file in order to compile the source file. When the
compilation is successful, the compiled version of the source file is created. This
compiled version is called bytecode, and the file that contains bytecode is called a
bytecode file. The name of the compiler-generated bytecode file will have the suffix
.class while its prefix is the same as the one for the source file.
When any error occurs in a program, an error message will be displayed. If the
sample program contains no errors in syntax, then instead of an error message, you
will get nothing or a message stating something like “Compiled successfully.” To see
what kind of error messages are displayed, try compiling the following program.
We purposely introduced three errors. Can you find them? Make sure to compile the
correct Ch2Sample1 again before you proceed to the next step.
/*
Chapter 2 Sample Program: Displaying a Window
File: Ch2Sample.java
*/
import javax.swing.*;
class Ch2Samplel {
public static void main( String[] args ) {
JFrame myWindow;
myWindow = new JFrame();
be 00 03 00 2d 00 1f 08 00 12 07 00 0c ..兰....–........
000010 07 00 15 07 00 13 0a 00 04 00 08 0a 00 03 00 07 ................
000020 0c 00 19 00 1c 0c 00 17 00 14 01 00 04 74 68 69 .............thi
000030 73 01 00 0d 43 6f 6e 73 74 61 6e 74 56 61 6c 75 s...ConstantValu
000040 65 01 00 12 4c 6f 63 61 6c 56 61 72 69 61 62 6c e...LocalVariabl
000050 65 54 61 62 6c 65 01 00 0e 6d 79 46 69 72 73 74 eTable...myFirst
000060 50 72 6f 67 72 61 6d 01 00 0a 45 78 63 65 70 74 Program...Except
000070 69 6f 6e 73 01 00 0f 4c 69 6e 65 4e 75 6d 62 65 ions...LineNumbe
000080 72 54 61 62 6c 65 01 00 0a 53 6f 75 72 63 65 46 rTable...Source
000090 69 6c 65 01 00 0e 4c 6f 63 61 6c 56 61 72 69 61 ile...LocalVaria
0000a0 62 6c 65 73 01 00 04 43 6f 64 65 01 00 0b 49 20 bles...Code...I
0000b0 4c 6f 76 65 20 4a 61 76 61 01 00 13 4a 61 76 61 Love Java...Java
0000c0 42 6f 6f 6b 2f 4d 65 73 73 61 67 65 42 6f 78 01 Book/MessageBox.
0000d0 00 15 28 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 ..(Ljava/lang/St
0000e0 72 69 6e 67 3b 29 56 01 00 10 6a 61 76 61 2f 6c ring;)V...java/1
0000f0 61 6e 67 2f 4f 62 6a 65 63 74 01 00 04 6d 61 69 ang/Object...mai
000100 6e 01 00 07 64 69 73 70 6c 61 79 01 00 16 28 5b n...display...([
000110 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e Ljava/lang/Strin
000120 67 3b 29 56 01 00 06 3c 69 6e 69 74 3e 01 00 10 g;)V...init...
000130 4c 6d 79 46 69 72 73 74 50 72 6f 67 72 61 6d 3b LmyFirstProgram;
000140 01 00 13 6d 79 46 69 72 73 74 50 72 6f 67 72 61 ...myFirstProgra
000150 6d 2e 6a 61 76 61 01 00 03 28 29 56 01 00 04 61 m.java...()V...a
000160 72 67 73 01 00 13 5b 4c 6a 61 76 61 2f 6c 61 6e rgs...[Ljava/lan
000170 67 2f 53 74 72 69 6e 67 3b 00 00 00 02 00 03 00 g/String;.......
Compiler
Ch2Sample1.java
(source file)
Ch2Sample1.class
(bytecode file)
Editor
50 Chapter 2 Getting Started with Java
import javax.swing.*;
class Ch2Sample1 {
public static void main( String[] args ) {
JFrame myWindow;
myWindow = new JFrame();
myWindow.setSize(300, 200);
myWindow.setTitle(My First Java Program);
myWindow.setVisible(true);
}
}
Editor
(source file)
project file
bytecode
bytecode file
wu23399_ch02.qxd 12/12/06 17:26 Page 50
Bad Version
import javax.swing.*;
class Ch2Sample1 {
public static void main( String[]args ) {
myWindow = new JFrame();
myWindow.setSize( );
myWindow.setTitle(My First Java Program);
myWindow.setVisible(true)
}
}
Errors detected by the compiler are called compilation errors. Compilation
errors are actually the easiest type of errors to correct. Most compilation errors are
due to the violation of syntax rules.
Step 3
Execute the bytecode file. A Java interpreter will go through the bytecode file and
execute the instructions in it. If your program is error-free, a window will appear on
the screen.
If an error occurs in running the program, the interpreter will catch it and stop
its execution. Errors detected by the interpreter are called execution errors. If you
did not see the expected results, go back to the previous steps and verify that your
program is entered correctly. If you still do not see the expected results, then most
likely your development environment is not set up correctly. Please refer to other
sources of information for further help.
be 00 03 00 2d 00 1f 08 00 12 07 00 0c
..兰....–........
000010 07 00 15 07 00 13 0a 00 04 00 08
0a 00 03 00 07 ................
000020 0c 00 19 00 1c 0c 00 17 00 14 01
00 04 74 68 69 .............thi
000030 73 01 00 0d 43 6f 6e 73 74 61 6e
74 56 61 6c 75 s...ConstantValu
000040 65 01 00 12 4c 6f 63 61 6c 56 61
72 69 61 62 6c e...LocalVariabl
000050 65 54 61 62 6c 65 01 00 0e 6d 79
46 69 72 73 74 eTable...myFirst
000060 50 72 6f 67 72 61 6d 01 00 0a 45
78 63 65 70 74 Program...Except
000070 69 6f 6e 73 01 00 0f 4c 69 6e 65
4e 75 6d 62 65 ions...LineNumbe
000080 72 54 61 62 6c 65 01 00 0a 53 6f
75 72 63 65 46 rTable...Source
/*
Chapter 2 Sample Program: Displaying a Window
File: Ch2Sample.java
*/
import javax.swing.*;
class Ch2Samplel {
public static void main( String[] args ) {
JFrame myWindow;
myWindow = new JFrame();
Compiler Interpreter
Ch2Sample1.java
(source file)
Ch2Sample1.class
(bytecode file)
Running Program
Editor
2.3 Edit-Compile-Run Cycle 51
compilation
error
execution error
wu23399_ch02.qxd 12/12/06 17:26 Page 51
52 Chapter 2 Getting Started with Java
Unlike machine-language instructions, or machine code, Java bytecode is not tied
to any particular operating system or CPU. All we need to run the same Java pro-
grams on different operating systems is the Java interpreters for the desired oper-
ating systems. Currently, there are Java interpreters for Windows, Mac, Unix, and
other operating systems. A Java interpreter is also called a Java Virtual Machine
(JVM) because it is like a virtual machine that executes bytecode, whereas a CPU is
a real machine that executes machine code.
2.4 Sample Java Standard Classes
Eventually, you must learn how to define your own classes, the classes you will
reuse in writing programs. But before you can become adept at defining your own
classes, you must learn how to use existing classes. In this section, we will intro-
duce standard classes. Sample code using these classes helps us reinforce the core
object-oriented programming (OOP) concepts introduced in Chapter 1 with the
actual Java statements. Five standard classes we introduce here are JOptionPane,
System, Scanner String, Date, and SimpleDateFormat. It is not our objective here to
explain these classes fully. Rather, our objective is to get you started in writing
practical Java programs with a minimal explanation of some of the useful standard
classes. We will introduce additional capabilities of these classes as we progress
through the textbook. Although we will scratch only the surface of these classes in
this section, what we provide here should serve as a foundation for you to delve
more deeply into these classes. For a more detailed description, please consult the
documentation for the standard classes. The documentation for the standard classes
is commonly called Java API documentation, where API stands for application
programming interface.
To become a good object-oriented programmer,first you must learn how to use
predefined classes.
Please do not get alarmed by the number of standard classes we introduce here.
Although we cover five standard classes at once, we limit ourselves to the most
basic operations, so we won’t overwhelm you with too much information. Their
documentation can be located online at
http://java.sun.com/j2se/1.5.0/docs/api/index.html
wu23399_ch02.qxd 12/12/06 17:26 Page 52
2.4.1 Standard Output
When a program computes a result, we need a way to display this result to the
user of the program. One of the most common ways to do this in Java is to use
the console window. The console window is also called the standard output
window. We output data such as the computation results or messages to the con-
sole window via System.out. The System class includes a number of useful class
data values. One is an instance of the PrintStream class named out. Since this is
a class data value, we refer to it through the class name as System.out, and this
PrintStream object is tied to the console window (there’s exactly one console
window per program). Every data item we send to System.out will appear on this
console window. We call the technique to output data by using System.out the
standard output.
We use the print method to output a value. For example, executing the code
System.out.print(Hello, Dr. Caffeine.);
will result in the console window shown in Figure 2.10. The actual appearance of
the console window will differ depending on which Java development tool we use.
Despite the difference in the actual appearance, its functionality of displaying data
is the same among different Java tools.
2.4 Sample Java Standard Classes 53
console
window
System.out
standard
output
Note
Depending on the tool you
use, you may see additional
text such as
Press any key to continue...
or something similar to it. We
will ignore any text that may be
displayed automatically by the
system.
Figure 2.10 Result of executing System.out.print(Hello,Dr.Caffeine.).
System.out refers to a precreated PrintStream object we use to output data to the
console window.The actual appearance of the console window depends on which
Java tool we use.
wu23399_ch02.qxd 12/12/06 17:26 Page 53
The print method will continue printing from the end of the currently dis-
played output. Executing the following statements will result in the console window
shown in Figure 2.11.
System.out.print(How do you do? );
System.out.print(My name is );
System.out.print(Seattle Slew.);
Notice that they all appear on the same line. If we want them to appear on individual
lines, we can use the println method instead of print. The word println is a short-
hand for “print line.” Figure 2.12 shows the effect of the println method.
This concludes our quick introduction to System.out. We will be gradually in-
troducing additional techniques of outputting to the console window as they are
needed.
54 Chapter 2 Getting Started with Java
System.out.print(How do you do? );
System.out.print(My name is );
System.out.print(Seattle Slew.);
How do you do? My name is Seattle Slew.
Code
Output
Note: Because the actual appearance of the console window
is different depending on the Java development tool
you use, we use a generic picture for the console
window in the diagrams.
Figure 2.11 Result of executing three consecutive print methods. The print method continues the printing
from the currently displayed output.
How do you do?
My name is
Seattle Slew.
System.out.println(How do you do? );
System.out.println(My name is );
System.out.println(Seattle Slew.);
Code
Output
Figure 2.12 Result of executing three consecutive println methods.The println method will skip to the next
line after printing out its argument.
wu23399_ch02.qxd 1/11/07 11:47 Page 54
2.4 Sample Java Standard Classes 55
1. Write a Java statement to display the text I Love Java in the console window.
2. Write statements to display the following shopping list in the console
window. Don’t forget to include blank spaces so the item names appear
indented.
Shopping List:
Apple
Banana
Low-fat Milk
2.4.2 String
The textual values we passed to the print method or the constructor of the JFrame
class are instances of the String class. A sequence of characters separated by double
quotes is String constants. As String is a class, we can create an instance and give it
a name. For example,
String name;
name = new String(Jon Java);
will result in a situation as follows:
Unlike in other classes, the explicit use of new to create an instance is optional
for the String class. We can create a new String object, for example, in this way:
String name;
name = Decafe Latte;
There are close to 50 methods defined in the String class. We will introduce
three of them here: substring, length, and indexOf. We can extract a substring from
a given string by specifying the beginning and ending positions. For example,
String text;
text = Espresso;
System.out.print(text.substring(2, 7));
will display the dialog shown in Figure 2.13.
name
Jon Java
:String
substring
wu23399_ch02.qxd 12/12/06 17:26 Page 55
Notice the use of method composition in the last statement, where the result of
a method call is used as an argument in another method call. In the statement
System.out.print (text.substring(2,7));
the result of method call
text.substring(2,7)
is passed as an argument when calling the showMessageDialog method. The sample
statement is equivalent to
String tempStr;
tempStr = text.substring(2,7);
System.out.print(tempStr);
Individual characters in a String object are indexed from 0, as illustrated in
Figure 2.14. The first argument of the substring method specifies the position of the
first character, and the second argument specifies the value that is 1 more than the
56 Chapter 2 Getting Started with Java
press
String text;
text = Espresso;
System.out.print(text.substring(2,7));
Code
Output
Figure 2.13 Result of extracting and displaying the substring of Expresso from index position’s 2 to 6.
The index position of the first character in a string is 0.
String text;
text = Espresso;
E s p r e s s o
0 1 2 3 4 5 6 7
Figure 2.14 Individual characters in a string are numbered from 0.
method
composition
wu23399_ch02.qxd 1/11/07 11:47 Page 56
An error will result if you pass invalid arguments, such as negative values, the sec-
ond argument larger than the number of characters in a string, or the first argument
larger than the second argument.
We can find out the number of characters in a String object by using the length
method. For example, if the name text refers to a string Espresso, then
text.length()
will return the value 8, because there are eight characters in the string. Here are
some more examples:
2.4 Sample Java Standard Classes 57
p r e s s
0 1 2 3 4
E s p r e s s o
0 1 2 3 4 5 6 7
text.substring(2, 7)
Figure 2.15 The effect of the substring method is shown.Notice that a new string is created, and the
original string remains intact.
length
text.substring( 6, 8 ) so
text.substring( 0, 8 ) Espresso
text.substring( 1, 5 ) spre
text1 = ; //empty string
text2 = Hello;
text3 = Java;
text1.length( ) 0
text2.length( ) 5
text3.length( ) 4
position of the last character. Figure 2.15 shows how the substring method works.
Here are some more examples:
wu23399_ch02.qxd 12/12/06 17:26 Page 57
To locate the index position of a substring within another string, we use the
indexOf method. For example, if the name text refers to a string I Love Java, then
text.indexOf(Love)
will return the value 2, the index position of the first character of the designated
string Love. If the searched substring is not located in the string, then 1 is returned.
Notice that the search is done in a case-sensitive manner. Thus,
text.indexOf(java)
will return 1. If there is more than one occurrence of the same substring, the index
position of the first character of the first matching substring is returned. Here are
some more examples:
58 Chapter 2 Getting Started with Java
indexOf
string
concatenation
text = I Love Java and Java loves me.;
text.indexOf(J) 7
text.indexOf(love) 21
text.indexOf(ove) 3
text.indexOf(ME) -1
3 7 21
Beyond the three methods we cover here and the remaining methods of the
String class, we have one very useful string operation in Java called string concate-
nation. We can create a new string from two strings by concatenating the two
strings. We use the plus symbol () for string concatenation. Here are the examples:
text1 = Jon;
text2 = Java;
text1 + text2 JonJava
text1 +   + text2 Jon Java
How are you,  + text1 + ?
How are you, Jon?
wu23399_ch02.qxd 12/12/06 17:26 Page 58
The sample class Ch2StringProcessing divides the given full name into the first
and last names and displays the number of letters in the last name.
2.4 Sample Java Standard Classes 59
/*
Chapter 2 Sample Program: Simple String Processing
File: Ch2StringProcessing.java
*/
class Ch2StringProcessing {
public static void main( String[] args ) {
String fullName, firstName, lastName, space;
fullName = new String(Decafe Latte);
space = new String( );
firstName = fullName.substring(0, fullName.indexOf(space));
lastName = fullName.substring(fullName.indexOf(space) + 1,
fullName.length());
System.out.println(Full Name:  + fullName);
System.out.println(First:  + firstName);
System.out.println(Last:  + lastName);
System.out.println(Your last name has  + lastName.length( )
+  characters.);
}
}
1. What will be the value of mystery when the following code is executed?
String text, mystery;
text = mocha chai latte;
mystery = text.substring(1,5);
2. What will be displayed on the message dialog when the following code is
executed?
String text = I, Claudius;
System.out.println(text.indexOf(I) );
wu23399_ch02.qxd 12/12/06 17:26 Page 59
3. What will be displayed on the message dialog when the following code is
executed?
String text = Augustus;
System.out.println(text.length());
4. What will be the value of text3 when the following code is executed?
String text1 = a + b;
String text2 = c;
String text3 = text1 + text2 + text1;
2.4.3 Date and SimpleDateFormat
The Date class is used to represent a time instance to a millisecond (one-thousandth
of a second) precision. This class is in the java.util package. When a new Date
object is created, it is set to the time it is created (the current time is determined by
reading the time maintained by the operating system on your machine). The Date
class includes the toString method that converts its internal format to a string repre-
sentation, which we can use to display the time. For example, executing the code
Date today;
today = new Date( );
System.out.println(today.toString());
will display the current time in this format:
Wed Jan 28 15:05:18 PDT 2006
Notice that the current time, when converted to a string format, includes the
date information also. Internally, the time is kept as an elapsed time in milliseconds
since the standard base time known as the epoch, which is January 1, 1970,
00:00:00 GMT (Greenwich Mean Time).
60 Chapter 2 Getting Started with Java
Why is the class called Date when its purpose is to keep track of time? The reason
is historical. In the older versions of Java, prior to JDK 1.1, the Date class was
indeed used to manipulate the year, month, and day components of the current
time. However, the way they are implemented was not amenable to internation-
alization.With the newer versions of Java, we use the GregorianCalendar class for
date manipulation.The GregorianCalendar class is explained in Chapter 3.
If we do not like the default format, say we want to display only the month and
year or only the hours and minutes in the AM/PM designation, then we can use the
SimpleDateFormat class. This class is in the java.text package. For example, if we
wu23399_ch02.qxd 12/12/06 17:26 Page 60
want to display the month, day, and year in the MM/dd/yy shorthand format, such
as 07/04/03, we write
Date today;
SimpleDateFormat sdf;
today = new Date( );
sdf = new SimpleDateFormat(MM/dd/yy);
System.out.println(sdf.format(today));
If today is June 28, 2006, the code will display the date as
06/28/06
Notice the format designation is done by passing the formatting string when a
new SimpleDateFormat object is created. The letters in the formatting string are case-
sensitive. The formatting string in this example must be MM/dd/yy, and the letters d
and y must be in lowercase. By increasing the number of formatting letters, we can
change the length of the information, say, 2006 instead of 06. In case of the month,
we change it from the number to a name. For example, when we change sdf to
sdf = new SimpleDateFormat(MMMM dd, yyyy);
the dialog will display
June 28, 2006
If we want to display which day of the week today is, we can use the letter E as in
Date today;
SimpleDateFormat sdf;
today = new Date( );
sdf = new SimpleDateFormat(EEEE);
System.out.println(Today is  + sdf.format(today));
Table 2.1 lists the common letters used in the formatting for SimpleDate-
Format. For more details, please consult the Java API documentation.
2.4 Sample Java Standard Classes 61
Table 2.1 is provided solely for the purpose of quick reference when you start
using the class in real programs.Nobody expects you to remember all those sym-
bols. What is important here is for you to grasp the key OOP concepts and the
fundamental way in which objects and classes are used, not to memorize minute
details that nobody remembers.
wu23399_ch02.qxd 12/12/06 17:26 Page 61
If you do not pass any string when creating a new SimpleDataFormat object,
the default formatting is used. The sample Ch2DateDisplay class displays today’s
date, using the default and programmer-designated format.
62 Chapter 2 Getting Started with Java
Table
Table 2.1
Some common formatting symbols for SimpleDateFormat and their
meanings.Please check the Java API documentation for full details.
Symbol Meaning Value Sample
y Year Number yyyy → 2002
M Month in year Text or number MM → 10
MMM → Oct
MMMM → October
d Day in month Number dd → 20
D Day in year Number DDD → 289
h Hour in AM/PM Number hh → 09
H Hour in day (0–23) Number HH → 17
a AM/PM marker Text a → AM
m Minutes in hour Number mm → 35
s Seconds in minute Number ss → 54
S Millisecond Number mmm → 897
E Day in week Text E → Sat
EEEE → Saturday
/*
Chapter 2 Sample Program: Displays Formatted Date Information
File: Ch2DateDisplay.java
*/
import java.util.*; //for Date
import java.text.*; //for SimpleDateFormat
class Ch2DateDisplay {
public static void main( String[] args ) {
Date today;
SimpleDateFormat simpleDF1,
simpleDF2;
today = new Date();
wu23399_ch02.qxd 12/12/06 17:26 Page 62
simpleDF1 = new SimpleDateFormat( );
simpleDF2 = new SimpleDateFormat (EEEE MMMM dd, yyyy);
//Default short format display
System.out.println(Today is  + simpleDF1.format(today) );
//Programmer-designated long format display
System.out.println(Today is  + simpleDF2.format(today) );
}
}
2.4 Sample Java Standard Classes 63
1. Write a code fragment to display today’s date in the 07-04-2002 format.
2. What will be displayed on the message dialog when the following code is
executed if today is July 4, 1776?
Date today;
SimpleDateFormat sdf;
today = new Date( );
sdf = new SimpleDateFormat(MMM dd, yyyy);
System.out.println(Today is  + sdf.format(today));
2.4.4 Standard Input
Analogous to System.out for output, we have System.in for input. We call the tech-
nique to input data using System.in standard input. System.in accepts input from
the keyboard. We also use the term console input to refer to standard input. Using
System.in for input is slightly more complicated than using System.out for output.
System.in is an instance of the InputStream class that provides only a facility to
input 1 byte at a time with its read method. However, multiple bytes are required to
represent common types of data such as strings. The Scanner class from the java.util
package provides a necessary input facility to accommodate various input routines.
We limit our discussion here to input of string values. We extend our discussion to
input of numerical values in Chapter 3.
To input data from the standard input by using a Scanner object, we first cre-
ate it by passing System.in as follows:
import java.util.*;
...
Scanner scanner;
scanner = new Scanner(System.in);
System.in
standard input
console input
Scanner
wu23399_ch02.qxd 12/12/06 17:27 Page 63
Once we have a Scanner object, then we can input a single word by using its next
method. Here’s code to input the first name of a person:
Scanner scanner = new Scanner(System.in);
String firstName;
//prompt the user for input
System.out.print(Enter your first name: );
firstName = scanner.next( );
System.out.println(Nice to meet you,  + firstName + .);
The user interaction of this sample code is shown in Figure 2.16. In the dia-
gram, the characters entered by the user are displayed in the console window as they
are typed in, so the user can see what’s been entered. Printing out the values just en-
tered is called echo printing. The string input is processed until the Enter (or Return)
key is pressed, so we can erase the characters by pressing the Backspace key while
entering the data.
Now let’s consider the case in which we want to input both the first name
and the last name. We can follow the sample code and input them one by one as
follows:
Scanner scanner = new Scanner(System.in);
String firstName, lastName;
System.out.print(Enter your first name: );
firstName = scanner.next( );
64 Chapter 2 Getting Started with Java
Figure 2.16 Sample interaction using System.in with Scanner and System.out. Keyboard input is echo-
printed in the console window.
Enter your first name: George ENTER
Nice to meet you, George.
System.out.print(Enter your first name: );
firstName = scanner.next( );
System.out.println(Nice to meet you,  + firstName + .);
This is entered by the user and echo
printed by the system.To distinguish
the input and output in the diagram,the
input is displayed in a different color.
This icon shows the pressing of
the Enter (Return) key.
Note: The console window used by your Java tool
may or may not use a different color for echo printing.
echo printing
wu23399_ch02.qxd 1/12/07 10:26 Page 64
Bad Version
System.out.print(Enter your last name: );
lastName = scanner.next( );
System.out.println(Your name is  + firstName +
 + lastName + .);
Enter your first name: George
Enter your last name: Washington
Your name is George Washington.
What can we do if we want input both the first name and the last name
together as a single input? Consider the following (wrong) code:
Scanner scanner = new Scanner(System.in);
String fullName;
System.out.print(Enter your first and last names: );
fullName = scanner.next( );
System.out.println(Your name is  + fullName + .);
Here’s a sample interaction of a user entering both the first name and the last name
on a single line:
Enter your first and last name: George Washington
Your name is George.
What happened to the last name? The blank space between the first name and the
last name is treated as a delimiter. So the system has accepted the characters up to,
but not including, the blank space as the input value. Because we know there are
first and last names, we can input them individually as follows:
Scanner scanner = new Scanner(System.in);
String first, last;
System.out.print(Enter your first and last name: );
first = scanner.next( );
last = scanner.next( );
System.out.println(Your name is  + first +  
+ last + .);
Enter your first and last name: George Washington
Your name is George Washington.
Instead of treating each word individually, it is possible to enter a set of words
as a single input. To do so, we must reset the delimiter to other than the blank space.
Any character can be set as a delimiter, but since we want to input the whole line as a
ENTER
ENTER
ENTER
ENTER
2.4 Sample Java Standard Classes 65
wu23399_ch02.qxd 1/12/07 10:26 Page 65
single input, it is most reasonable to set the delimiter to the Enter key. Here’s how we
change the delimiter to the Enter key and accept the complete line as a single input:
Scanner scanner = new Scanner(System.in);
String lineSeparator
= System.getProperty(line.separator);
scanner.useDelimiter(lineSeparator);
String quote; Note we’re using println here.
System.out.println(Enter your favorite quote: );
quote = scanner.next( );
System.out.println(You entered:  + quote);
Enter your favorite quote:
There never was a good war or a bad peace.
You entered: There never was a good war or a bad peace.
We override the default delimiter by calling the useDelimiter method and pass
the appropriate argument. We use the class method getProperty of the System class
to retrieve the actual sequence of characters for the Enter key that is specific to the
platform which our program is running. For the Windows platform, for instance, we
can call the useDelimiter method as
scanner.useDelimiter(rn);
But such code is guaranteed only to work on the Windows platform. It may or may
not work on other platforms. To make the code general enough to work on all plat-
forms, we use System.getProperty. Incidentally, the backslash character () is called
a control character or an escape character. We’ll examine the use of control char-
acters later in the book.
ENTER
66 Chapter 2 Getting Started with Java
1. Write a code to input the last name of a user.
2. Show the content of the console window when the following code is executed
and the text Barbaro is entered:
Scanner scanner = new Scanner(System.in);
String winner;
System.out.print(
Enter the name of the derby winner: );
winner = scanner.next( );
System.out.println(2006 Kentucky Derby Winner is 
+ name + .);
wu23399_ch02.qxd 12/12/06 17:27 Page 66
2.5 Sample Development 67
Printing the Initials
Now that we have acquired a basic understanding of Java application programs, let’s
write a new application.We will go through the design,coding,and testing phases of the
software life cycle to illustrate the development process. Since the program we develop
here is very simple,we can write it without really going through the phases.However,it is
extremely important for you to get into a habit of developing a program by following the
software life cycle stages. Small programs can be developed in a haphazard manner, but
not large programs.We will teach you the development process with small programs first,
so you will be ready to use it to create large programs later.
We will develop this program by using an incremental development technique,
which will develop the program in small incremental steps. We start out with a bare-
bones program and gradually build up the program by adding more and more code to
it. At each incremental step, we design, code, and test the program before moving on
to the next step. This methodical development of a program allows us to focus our at-
tention on a single task at each step, and this reduces the chance of introducing errors
into the program.
Problem Statement
We start our development with a problem statement. The problem statement for our
sample programs will be short,ranging from a sentence to a paragraph,but the problem
statement for complex and advanced applications may contain many pages. Here’s the
problem statement for this sample development exercise:
Write an application that asks for the user’s first, middle, and last names and
replies with the user’s initials.
Overall Plan
Our first task is to map out the overall plan for development.We will identify classes nec-
essary for the program and the steps we will follow to implement the program.We begin
with the outline of program logic.For a simple program such as this one,it is kind of obvi-
ous; but to practice the incremental development, let’s put down the outline of program
flow explicitly.We can express the program flow as having three tasks:
1. Get the user’s first,middle,and last names.
2. Extract the initials to formulate the monogram.
3. Output the monogram.
Having identified the three major tasks of the program, we will now identify the
classes we can use to implement the three tasks. First, we need an object to handle the
input. At this point, we have learned about only the Scanner class, so we will use it
here. Second, we need an object to display the result. Again, we will use System.out, as
it is the only one we know at this point for displaying a string value. For the string
Sample Development
2.5 Sample Development
program
tasks
wu23399_ch02.qxd 12/12/06 17:27 Page 67
68 Chapter 2 Getting Started with Java
2.5 Sample Development—continued
manipulation,we will use the String class.Finally,we will use these classes from the main
class,which we will call Ch2Monogram.Let’s summarize these in a design document:
Design Document: Monogram
Class Purpose
Ch2Monogram The main class of the program.
Scanner The next method is used for getting the full name.
String The class is used for string manipulation,extracting initials
from the first,middle,and last names.
(PrintStream) The standard output window is used for displaying the resulting
System.out monogram.
program
classes
The program diagram of Ch2Monogram is shown in Figure 2.17.Keep in mind that
this is only a preliminary design.Although we are not going to see any changes made to
this design document because this sample application is very simple, changes to the
design document are expected as the programs we develop become larger and more
complex.The preliminary document is really a working document that we will modify and
expand as we progress through the development steps.
Before we can actually start our development,we must sketch the steps we will fol-
low to develop the program.There is more than one possible sequence of steps to develop
Figure 2.17 The program diagram for Ch2Monogram.
Ch2Monogram
String PrintStream
(System.out)
Scanner
wu23399_ch02.qxd 12/12/06 17:27 Page 68
a program, and the number of possible sequences will increase as the program becomes
more complex.For this program,we will develop the program in two steps:
1. Start with the program template and add code to get input.
2. Add code to compute and display the monogram.
Step 1 Development: Getting Input
The problem states that the program is to input the user’s name and display its initials. It
does not specify how, so in the design stage, we will decide how to do this. Since, at this
point, we know only one way to input data, that is, using the Scanner class, we will use it
here.But in which form shall we input three pieces of data? There are two possible design
alternatives.
In the first design,we will input them separately:
String firstName, middleName, lastName;
Scanner scanner = new Scanner(System.in);
System.out.print(First Name: );
firstName = scanner.next( );
System.out.print(Middle Name: );
middleName = scanner.next( );
System.out.print(Last Name: );
lastName = scanner.next( );
In the second design,we will input them together:
String fullName;
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
System.out.print(Full Name: );
fullName = scanner.next( );
Which design is better? There is never“one correct answer”to the design problems.
We have to select the one from the possible alternatives that satisfies the different crite-
ria most effectively in a given situation. The criteria may include the user’s needs and
preferences, faster performance, development costs, time contraints, and other factors.
For example, in one situation, we may decide to forgo some great user interface features
so the development can be completed under budget.
In this sample development, we will consider the alternative designs from the
overall quality of the program’s user interface. In other words, we want to make our pro-
gram as user-friendly as possible.We want our users to have a pleasant experience using
our program. The program should not be cumbersome to use, or else the users will get
very frustrated in using the program. Which design would give the better user experi-
ence? In the first approach,the user enters the information separately with three dialogs,
2.5 Sample Development 69
develop-
ment steps
step 1
design
alternative
design 1
alternative
design 2
wu23399_ch02.qxd 12/12/06 19:29 Page 69
2.5 Sample Development—continued
while in the second approach, the user enters the information together with one dialog.
We choose the second approach because it allows quicker data entry,and in general,it is
more natural to treat the name as a single entity than as three separate entitites. If we
were to enter the name,address,and phone number,then we would use three dialogs as
they are three separate entities. In this situation, we consider the first, middle, and last
names as part of a single entity.
Notice that the decision to enter the full name by using one dialog makes our task
as the programmer slightly more difficult because we need to extract the first, middle,
and last names from a single string. In the first approach, as we get the first, middle, and
last names separately,there’s no such need.So,if we consider strictly the ease of develop-
ment, the first approach is better. It is important to remember, however, that we are de-
veloping the program for the sake of the users,not for ourselves.
70 Chapter 2 Getting Started with Java
We develop programs for the sake of users,not for ourselves.Ease of use has higher
priority than ease of development.
Let’s implement the second design alternative. In the code, notice the use of the output
statement that prints the string entered by the user.This printing of the input is another
form of echo printing (introduced in Section 2.4.4). By echo printing, we verify that the
input value is indeed read in correctly.
/*
Chapter 2 Sample Program: Displays the Monogram
File: Step1/Ch2Monogram.java
/*
import java.util.*;
class Ch2Monogram {
public static void main(String[] args) {
String name;
Scanner scanner = new Scanner(System.in);
step 1 code
wu23399_ch02.qxd 12/12/06 17:27 Page 70
scanner.useDelimiter(System.getProperty(line.separator));
System.out.print(Enter your full name (first, middle, last):);
name = scanner.next( );
System.out.println(Name entered:  + name);
}
}
2.5 Sample Development 71
After the program is written, we test the program to verify that the program runs as in-
tended. The step 1 program may seem so trivial and not so useful,but it does serve a very
useful purpose. Successful execution of this program verifies that the program setup is
okay, the necessary packages are imported, and the objects are declared correctly. Since
this program is very simple, there’s not much testing strategy we can employ other than
simply running it.For subsequent sample programs,however,the testing strategy will be
more involved.After the step 1 program is compiled and executed correctly,we move on
to step 2.
Step 2 Development: Computing and Displaying the Monogram
The next task is to extract initials from the input string. First, because of our limited
knowledge of programming at this point, we will assume the input is correct. That is,
the input string contains first, middle, and last names, and they are separated by single
blank spaces. Second, there are many possible solutions, but we will solve this problem
by using only the methods covered in this chapter. Reviewing the string methods we
covered in this chapter and the Ch2String Processing class, we know that a sequence
of indexOf and substring methods can divide a string (full name) into two substrings
(first and last names). How can we adapt this technique to now divide a string (full
name) into three substrings (first, middle, and last names)? Aha! We apply the sequence
one more time, as shown in Figure 2.18.
Once we divide the input name into first,middle,and last names,extracting the ini-
tials is a fairly straightforward application of the indexOf method.We can extract the first
letter of a string as
str.substring(0, 1)
And the monogram can be formulated by concatenating three initials as
first.substring(0, 1)
+ middle.substring(0, 1)
+ last.substring(0, 1)
step 1 test
step 2
design
wu23399_ch02.qxd 12/12/06 17:27 Page 71
2.5 Sample Development—continued
Here’s our step 2 code:
72 Chapter 2 Getting Started with Java
Figure 2.18 Apply the two sequences of indexOf and substring methods to extract three substrings
from a given string.
General Idea
String name;
name = Jon Jay Java;
Jon Jay Java
Jon Jay Java
Jay Java
Actual Statements
Jon Jay Java
Jon Jay Java
Jay Java
first = name.substring(0,
name.indexOf( ));
name =
name.substring(name.indexOf( )+1,
name.length());
middle = name.substring(0,
name.indexOf( ));
last =
name.substring(name.indexOf( )+1,
name.length());
step 2 code
/*
Chapter 2 Sample Program: Displays the Monogram
File: Step2/Ch2Monogram.java
*/
wu23399_ch02.qxd 12/12/06 17:27 Page 72
import java.util.*;
class Ch2Monogram {
public static void main(String[] args) {
String name;
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
System.out.print(Enter your full name (first, middle, last):);
name = scanner.next( );
System.out.println(Name entered:  + name);
}
}
Summary 73
To verify the computation is working correctly, we run the program multiple times and
enter different names. Remember that we are assuming there is no error in input; that is,
first,middle,and last names are separated by single blank spaces.Since there are two sub-
tasks involved in this step, it is important to test them separately.To verify that the input
string is divided into three substrings correctly, we place the following temporary test
output statements.
System.out.println(First: + first);
System.out.println(Middle: + middle);
System.out.println(Last: + last);
These statements are not shown in the step 2 program listing,but they are included in the
actual sample code.
step 2 test
• The three basic components of a Java program are comments, import
statements, and class declarations.
• A Java program must have one class designated as the main class. The
designated main class must have the main method.
• An object must be declared and created before we can use it.
• To command an object or a class to perform a task, we send a message to it. We
use the expression calling a method synonymously with sending a message.
• A single name can be used to refer to different objects (of the same class) at
different times. An object with no reference will be returned to a system.
S u m m a r y
wu23399_ch02.qxd 12/12/06 17:27 Page 73
• We follow the edit-compile-run cycle to execute programs.
• A source file is compiled into a bytecode file by a Java compiler.
• A Java interpreter (also called a Java Virtual Machine) executes the bytecode.
• The standard classes introduced in this chapter are
JFrame SimpleDateFormat
Scanner String
Date System.out
System.in
• Table 2.2 lists the Java naming convention.
74 Chapter 2 Getting Started with Java
Table
Table 2.2 Standard naming convention for Java
Category Convention Example
Class
Instance
Constant
Package
Use an uppercase letter for the first letter of
the class names.If the name consists of
multiple words,the first letter of every word
is capitalized.
Use a lowercase letter for the first letter of
the object names.If the name consists of
multiple words,the first letter of every word
(except the first word) is capitalized.
(Note:Sample use of a constant will appear
in Chap.4.We include it here for
completeness and easy reference later.)
Use all uppercase letters.If the constant
consists of multiple words,the underscore
characters are used to separate the words.
Use all lowercase letters.
Customer
MainWindow
MyInputHandler
customer
inputHandler
myFirstApplication
DEFAULT_RATE
DEG_TO_RAD
CANCEL
java
game
finance
K e y C o n c e p t s
standard classes
program diagram
identifier
standard naming convention
new operator
garbage collection
comments
packages
dot notation
class declaration
method declaration
edit-compile-run cycle
source file
bytecode file
wu23399_ch02.qxd 12/12/06 17:27 Page 74
Exercises 75
E x e r c i s e s
1. Identify all errors in the following program (color highlighting is disabled):
/*
Program Exercise1
Attempting to display a frame window
//
import swing.JFrame;
class Exercise 1 {
public void Main() {
JFrame frame;
frame.setVisible(TRUE)
}
}
2. Identify all errors in the following program (color highlighting is disabled):
//
Program Exercise2
Attempting to display a frame of size 300 by 200 pixels
//
import Javax.Swing.*;
class two {
public static void main method() {
myFrame JFrame;
myFrame = new JFrame();
myFrame.setSize(300, 200);
myFrame.setVisible();
}
}
3. Identify all the errors in the following program (color highlighting is disabled):
/*
Program Exercise3
Attempting to display the number of characters
in a given input.
*/
class three {
public static void main( ) {
String input;
input = JOptionPane(input:);
wu23399_ch02.qxd 12/12/06 17:27 Page 75
System.out.print (Input has  +
input.length() +  characters);
}
}
4. Describe the purpose of comments. Name the types of comments available.
Can you include comment markers inside a comment?
5. What is the purpose of the import statement? Does a Java program always
have to include an import statement?
6. Show the syntax for importing one class and all classes in a package.
7. Describe the class that must be included in any Java application.
8. What is a reserved word? List all the Java reserved words mentioned in this
chapter.
9. Which of the following are invalid Java identifiers?
a. R2D2
b. Whatchamacallit
c. HowAboutThis?
d. Java
e. GoodChoice
f. 12345
76 Chapter 2 Getting Started with Java
g. 3CPO
h. This is okay.
i. thisIsReallyOkay
j. DEFAULT_AMT
k. Bad-Choice
l. A12345
10. Describe the steps you take to run a Java application and the tools you use in
each step. What are source files and bytecode files? What different types of
errors are detected at each step?
11. Describe the difference between object declaration and object creation. Use
a state-of-memory diagram to illustrate the difference.
12. Show a state-of-memory diagram after each of these statements is executed:
JFrame window1;
Resident res1, res2;
window1 = new JFrame();
res1 = new Resident( );
res2 = new Resident( );
13. Show a state-of-memory diagram after each of these statements is executed:
Person person1, person2;
person1 = new Person();
person2 = new Person();
person2 = new Person();
14. Which of these identifiers violate the naming convention for class names?
a. r2D2
b. whatchamacallit
c. Java
d. GoodName
e. CPO
f. ThisIsReallyOkay
g. java
h. badName
wu23399_ch02.qxd 12/12/06 17:27 Page 76
15. Which of these identifiers violate the naming convention for object names?
a. R2D2
b. isthisokay?
c. Java
d. goodName
Exercises 77
e. 3CPO
f. ThisIsReallyOkay
g. java
h. anotherbadone
16. For each of these expressions, determine its result. Assume the value of text
is a string Java Programming.
String text = Java Programming;
a. text.substring(0, 4)
b. text.length( )
c. text.substring(8, 12)
d. text.substring(0, 1) + text.substring(7, 9)
e. text.substring(5,6)
+ text.substring(text.length()-3, text.length())
17. Write a Java application that displays today’s date in this format: Sunday
November 10, 2002.
18. Write a Java application that displays a frame window 300 pixels wide and
200 pixels high with the title My First Frame. Place the frame so that its top
left corner is at a position 50 pixels from the top of the screen and 100 pixels
from the left of the screen. To position a window at a specified location, you
use the setLocation method, as in
//assume mainWindow is declared and created
frame.setLocation( 50, 50 );
Through experimentation, determine how the two arguments in the
setLocation method affect the positioning of the window.
19. Write a Java application that displays the two messages I Can Design and
And I Can Program, using two separate dialogs.
20. Write a Java application that displays the two messages I Can Design and
And I Can Program, using one dialog but in two separate lines.
21. Write a Java application that displays a very long message. Try a message
that is wider than the display of your computer screen, and see what
happens.
22. Because today’s computers are very fast, you will probably not notice any
discernible difference on the screen between the code
JFrame myWindow;
myWindow = new JFrame( );
myWindow.setVisible( true );
wu23399_ch02.qxd 12/12/06 17:27 Page 77
and
JFrame myWindow;
myWindow = new JFrame( );
myWindow.setVisible( true );
myWindow.setVisible( false );
myWindow.setVisible( true );
One way to see the disappearance and reappearance of the window is to put
a delay between the successive setVisible messages. Here’s the magic code
that puts a delay of 0.5 s:
try {Thread.sleep(500);} catch(Exception e) { }
The argument we pass to the sleep method specifies the amount of delay in
milliseconds [note: 1000 milliseconds (ms)  1 second (s)]. We will not
explain this magic code.
23. At the author’s website, you will find a Java package called galapagos. The
galapagos package includes a Turtle class that is modeled after Seymour
Papert’s logo. This Turtle has a pen, and when you move the Turtle, its pen
will trace the movement. So by moving a Turtle object, you can draw many
different kinds of geometric shapes. For example, this program commands a
Turtle to draw a square:
import galapagos.*;
class Square {
public static void main( String[] arg ) {
Turtle turtle;
turtle = new Turtle( );
turtle.move( 50 ); //move 50 pixels
turtle.turn( 90 ); //turn 90 deg counterclockwise
turtle.move( 50 );
turtle.turn( 90 );
turtle.move( 50 );
turtle.turn( 90 );
turtle.move( 50 );
}
}
Write a program to draw a triangle. Read the documentation and see
if you can find a way to draw the square in a different color and line
thickness.
24. Write a program to draw a star, using a Turtle from Exercise 23.
25. Write a program to draw a big letter J, using a Turtle from Exercise 23.
78 Chapter 2 Getting Started with Java
wu23399_ch02.qxd 12/12/06 17:27 Page 78
26. Using a Turtle from Exercise 23, write a Java application that displays the
text Hello as illustrated here:
27. Using a Turtle from Exercise 23 and employing the incremental development
steps, build a Java application that draws a house.
28. Add the moon and a tree to the house you drew in Exercise 27.
29. Follow the incremental development methodology explained in this chapter
to implement a program for the following problem statement. You must
clearly write down the program tasks, create a design document with class
descriptions, and draw the program diagram. Identify the development steps.
State any assumptions you must make about the input. Articulate any design
alternatives and justify your selection. Be sure to perform adequate testing at
the end of each development step.
Problem Statement: Write an application that asks the user for his or her
birth date and replies with the day of the week on which he or she was born.
We learned in this chapter that we can create a Date object for today’s date
by writing
import java.util.*;
...
Date today = new Date();
Hello Hello
Hello Hello
Hello
Exercises 79
wu23399_ch02.qxd 12/12/06 17:27 Page 79
To create a Date object for a date other than today, we can use the Date class
from the java.sql package. (A more general and flexible way to deal with a
date by using the GregorianCalendar class is introduced in Chap. 3.) Notice
that there are two distinct classes with the same name Date, but from
different packages—one from java.util and another from java.sql. To
distinguish the two, we will use the fully qualified names. To create a
new java.util.Date object, we can call the class method valueOf of the
java.sql.Date class with the string representation of a date. The string
representation must be in the format yyyy-MM-dd. For example, to create
a java.util.Date object for July 4, 1776, we write
java.util.Date bdate = java.sql.Date.valueOf(1776-07-04);
Notice that valueOf is a class method of the Date class in the java.sql
package. Calling it with a correct argument will return a java.util.Date object
for the specified date.
30. Repeat Exercise 29 for this problem statement:
Problem Statement: Write an application that asks the user for her or his
full name in the format
first middle last
and replies with the name in the format
last , first middle-initial.
where the last name is followed by comma and the middle initial is followed
by period.
For example, if the input is
Decafe Chai Latte
then the output is
Latte, Decafe C.
80 Chapter 2 Getting Started with Java
wu23399_ch02.qxd 12/12/06 17:27 Page 80
Numerical Data
O b j e c t i v e s
After you have read and studied this chapter,you should be able to
• Select proper types for numerical data.
• Write arithmetic expressions in Java.
• Evaluate arithmetic expressions,following the
precedence rules.
• Describe how the memory allocation works for
objects and primitive data values.
• Write mathematical expressions,using
methods in the Math class.
• Use the GregorianCalendar class in
manipulating date information such as year,
month,and day.
• Use the DecimalFormat class to format
numerical data.
• Convert input string values to numerical data.
• Input numerical data by using System.in and
output numerical data by using System.out.
• Apply the incremental development technique
in writing programs.
• (Optional) Describe how the integers and real
numbers are represented in memory.
81
3
wu23399_ch03.qxd 12/13/06 17:38 Page 81
82 Chapter 3 Numerical Data
I n t r o d u c t i o n
hen we review the Ch2Monogram sample program, we can visualize three tasks:
input, computation, and output. We view computer programs as getting input, per-
forming computation on the input data, and outputting the results of the computa-
tions. The type of computation we performed in Chapter 2 is string processing. In
this chapter, we will study another type of computation, the one that deals with
numerical data. Consider, for example, a metric converter program that accepts
measurements in U.S. units (input), converts the measurements (computation), and
displays their metric equivalents (output). The three tasks are not limited to numer-
ical or string values, though. An input could be a mouse movement. A drawing pro-
gram may accept mouse dragging (input), remember the points of mouse positions
(computation), and draw lines connecting the points (output). Selecting a menu item
is yet another form of input. For beginners, however, it is easiest to start writing
programs that accept numerical or string values as input and display the result of
computation as output.
We will introduce more standard classes to reinforce the object-oriented style
of programming. The Math class includes methods we can use to express mathe-
matical formulas. The DecimalFormat class includes a method to format numerical
data so we can display the data in a desired precision. The GregorianCalendar class
includes methods to manipulate the date. In Chapter 2, we performed String input
and output by using the standard input (Scanner) and output (System.out). We will
describe the input and output routines for numerical data in this chapter.
Finally, we will continue to employ the incremental development technique
introduced in Chapter 2 in developing the sample application, a loan calculator pro-
gram. As the sample program gets more complex, well-planned development steps
will smooth the development effort.
3.1 Variables
Suppose we want to compute the sum and difference of two numbers. Let’s call the
two numbers x and y. In mathematics, we say
x + y
and
x – y
To compute the sum and the difference of x and y in a program, we must first declare
what kind of data will be assigned to them. After we assign values to them, we can
compute their sum and difference.
Let’s say x and y are integers. To declare that the type of data assigned to them
is an integer, we write
int x, y;
W
wu23399_ch03.qxd 12/13/06 17:38 Page 82
When this declaration is made, memory locations to store data values for x and y are
allocated. These memory locations are called variables, and x and y are the names
we associate with the memory locations. Any valid identifier can be used as a vari-
able name. After the declaration is made, we can assign only integers to x and y. We
cannot, for example, assign real numbers to them.
3.1 Variables 83
variable
A variable has three properties:a memory location to store the value,the type of
data stored in the memory location,and the name used to refer to the memory
location.
Although we must say “x and y are variable names” to be precise, we will use the
abbreviated form “x and y are variables” or “x and y are integer variables” whenever
appropriate.
The general syntax for declaring variables is
data type variables ;
where variables is a sequence of identifiers separated by commas. Every variable
we use in a program must be declared. We may have as many declarations as we
wish. For example, we can declare x and y separately as
int x;
int y;
However, we cannot declare the same variable more than once; therefore, the sec-
ond declaration below is invalid because y is declared twice:
int x, y, z;
int y;
There are six numerical data types in Java: byte, short, int, long, float, and
double. The data types byte, short, int, and long are for integers; and the data types
float and double are for real numbers. The data type names byte, short, and others
are all reserved words. The difference among these six numerical data types is in
the range of values they can represent, as shown in Table 3.1.
A data type with a larger range of values is said to have a higher precision. For
example, the data type double has a higher precision than the data type float. The
tradeoff for higher precision is memory space—to store a number with higher pre-
cision, you need more space. A variable of type short requires 2 bytes and a variable
of type int requires 4 bytes, for example. If your program does not use many
integers, then whether you declare them as short or int is really not that critical. The
variable
declaration
syntax
six numerical
data types
higher
precision
wu23399_ch03.qxd 12/13/06 17:38 Page 83
difference in memory usage is very small and not a deciding factor in the program
design. The storage difference becomes significant only when your program uses
thousands of integers. Therefore, we will almost always use the data type int for in-
tegers. We use long when we need to process very large integers that are outside the
range of values int can represent. For real numbers, it is more common to use dou-
ble. Although it requires more memory space than float, we prefer double because
of its higher precision in representing real numbers. We will describe how the num-
bers are stored in memory in Section 3.10.
84 Chapter 3 Numerical Data
Table
Table 3.1 Java numerical data types and their precisions
†
No default value is assigned to a local variable. A local variable is explained on page 184 in Section 4.8.
‡
The character E indicates a number is expressed in scientific notation. This notation is explained on page 96.
Data Default
Type Content Value†
Minimum Value Maximum Value
byte Integer 0 128 127
short Integer 0 32768 32767
int Integer 0 2147483648 2147483647
long Integer 0 9223372036854775808 9223372036854775807
float Real 0.0 3.40282347E+38‡
3.40282347E+38
double Real 0.0 1.79769313486231570E+308 1.79769313486231570E+308
Application programs we develop in this book are intended for computers with
a large amount of memory (such as desktops or laptops), so the storage space is
not normally a major concern because we have more than enough. However,
when we develop applications for embedded or specialized devices with a very
limited amount of memory, such as PDAs, cellular phones, mobile robots for
Mars exploration, and others, reducing the memory usage becomes a major
concern.
Here is an example of declaring variables of different data types:
int i, j, k;
float numberOne, numberTwo;
long bigInteger;
double bigNumber;
At the time a variable is declared, it also can be initialized. For example, we may
initialize the integer variables count and height to 10 and 34 as in
int count = 10, height = 34;
wu23399_ch03.qxd 12/13/06 17:38 Page 84
We assign a value to a variable by using an assignment statement. To assign
the value 234 to the variable named firstNumber, for example, we write
firstNumber = 234;
Be careful not to confuse mathematical equality and assignment. For example, the
following are not valid Java code:
4 + 5 = x;
x + y = y + x;
The syntax for the assignment statement is
variable = expression ;
where expression is an arithmetic expression, and the value of expression is
assigned to the variable. The following are sample assignment statements:
sum = firstNumber + secondNumber;
solution = x * x - 2 * x + 1;
average = (x + y + z) / 3.0;
We will present a detailed discussion of arithmetic expressions in Section 3.2. One
key point we need to remember about variables is the following:
3.1 Variables 85
As we mentioned in Chapter 2, you can declare and create an object just as you
can initialize variables at the time you declare them. For example, the declaration
Date today = new Date();
is equivalent to
Date today;
today = new Date();
assignment
statement
syntax
assignment
statement
Before using a variable,first we must declare and assign a value to it.
The diagram in Figure 3.1 illustrates the effect of variable declaration and as-
signment. Notice the similarity with this and memory allocation for object declara-
tion and creation, illustrated in Figure 2.4 on page 36. Figure 3.2 compares the two.
wu23399_ch03.qxd 12/13/06 17:38 Page 85
What we have been calling object names are really variables. The only difference
between a variable for numbers and a variable for objects is the contents in the
memory locations. For numbers, a variable contains the numerical value itself; and
for objects, a variable contains an address where the object is stored. We use an
arrow in the diagram to indicate that the content is an address, not the value itself.
86 Chapter 3 Numerical Data
State of Memory
int firstNumber, secondNumber;
firstNumber = 234;
secondNumber = 87;
firstNumber = 234;
secondNumber = 87;
The variables firstNumber and secondNumber
are declared and set in memory.
A
B
int firstNumber, secondNumber; after is executed
A
firstNumber
secondNumber
Values are assigned to the variables firstNumber
and secondNumber.
after is executed
B
firstNumber
secondNumber
234
87
Figure 3.1 A diagram showing how two memory locations (variables) with names firstNumber and
secondNumber are declared,and values are assigned to them.
Object names are synonymous with variables whose contents are references to
objects (i.e.,memory addresses).
Figure 3.3 contrasts the effect of assigning the content of one variable to an-
other variable for numerical data values and for objects. Because the content of a
variable for objects is an address, assigning the content of a variable to another
makes two variables that refer to the same object. Assignment does not create a new
object. Without executing the new command, no new object is created. We can view
the situation in which two variables refer to the same object as the object having two
distinct names.
wu23399_ch03.qxd 12/13/06 17:38 Page 86
For numbers, the amount of memory space required is fixed. The values for
data type int require 4 bytes, for example, and this won’t change. However, with ob-
jects, the amount of memory space required is not constant. One instance of the
Account class may require 120 bytes, while another instance of the same class may
require 140 bytes. The difference in space usage for the account objects would
occur if we had to keep track of checks written against the accounts. If one account
has 15 checks written and the second account has 25 checks written, then we need
more memory space for the second account than for the first account.
We use the new command to actually create an object. Remember that declar-
ing an object only allocates the variable whose content will be an address. On the
3.1 Variables 87
customer = new Customer();
customer = new Customer();
number = 237;
number = 35;
Numerical Data Object
int number; Customer customer;
int number;
number = 35;
number = 237;
Customer customer;
customer = new Customer();
customer = new Customer();
number customer
customer
customer
number 237
number 35
:Customer
:Customer :Customer
int number;
number = 237;
number = 35;
Customer customer;
customer = new Customer();
customer = new Customer();
Figure 3.2 A difference between object declaration and numerical data declaration.
wu23399_ch03.qxd 12/13/06 17:38 Page 87
88 Chapter 3 Numerical Data
Numerical Data Object
number1 = 237;
number2 = number1;
int number1, number2;
alan = new Professor();
turing = alan;
Professor alan, turing;
number2
number1
turing
alan
number2
number1
turing
alan
number1 = 237;
int number1, number2;
alan = new Professor();
Professor alan, turing;
number2 = number1; turing = alan;
:Professor
:Professor
number2
number1
turing
alan
number1 = 237;
int number1, number2;
alan = new Professor();
Professor alan, turing;
number2 = number1; turing = alan;
237
237
237
Figure 3.3 An effect of assigning the content of one variable to another.
other hand, we don’t “create” an integer because the space to store the value is
already allocated at the time the integer variable is declared. Because the contents
are addresses that refer to memory locations where the objects are actually stored,
objects are called reference data types. In contrast, numerical data types are called
primitive data types.
reference
versus
primitive data
types
wu23399_ch03.qxd 12/13/06 17:38 Page 88
3.1 Variables 89
In addition to the six numerical data types, there are two nonnumerical primitive
data types. The data type boolean is used to represent two logical values true and
false. For example,the statements
boolean raining;
raining = true;
assign the value true to a boolean variable raining. We will explain and start using
boolean variables beginning in Chapter 5. The second nonnumerical primitive
data type is char (for character). It is used to represent a single character (letter,
digit, punctuation marks, and others). The following example assigns the upper-
case letter A to a char variable letter:
char letter;
letter = 'A';
A char constant is designated by single quotes. We will study the char data type in
Chapter 9 on string processing.
1. Why are the following declarations all invalid (color highlighting is disabled)?
int a, b, a;
float x, int;
float w, int x;
bigNumber double;
2. Assuming the following declarations are executed in sequence, why are the
second and third declarations invalid?
int a, b;
int a;
float b;
3. Name six data types for numerical values.
4. Which of the following are valid assignment statements (assuming the
variables are properly declared)?
x = 12;
12 = x;
y + y = x;
y = x + 12;
5. Draw the state-of-memory diagram for the following code.
Account latteAcct, espressoAcct;
latteAcct = new Account();
espressoAcct = new Account();
latteAcct = espressoAcct;
wu23399_ch03.qxd 12/13/06 17:38 Page 89
3.2 Arithmetic Expressions
An expression involving numerical values such as
23 + 45
is called an arithmetic expression, because it consists of arithmetic operators and
operands. An arithmetic operator, such as + in the example, designates numerical
computation. Table 3.2 summarizes the arithmetic operators available in Java.
Notice how the division operator works in Java. When both numbers are inte-
gers, the result is an integer quotient. That is, any fractional part is truncated. Divi-
sion between two integers is called integer division. When either or both numbers
are float or double, the result is a real number. Here are some division examples:
90 Chapter 3 Numerical Data
arithmetic
operator
integerdivision
Table
Table 3.2 Arithmetic operators
Java Value
Operation Operator Example (x  10,y  7,z  2.5)
Addition + x + y 17
Subtraction – x – y 3
Multiplication * x * y 70
Division / x / y 1
x / z 4.0
Modulo division % x % y 3
(remainder)
Division Operation Result
23 / 5 4
23 / 5.0 4.6
25.0 / 5.0 5.0
The modulo operator returns the remainder of a division. Although real num-
bers can be used with the modulo operator, the most common use of the modulo
operator involves only integers. Here are some examples:
Modulo Operation Result
23 % 5 3
23 % 25 23
16 % 2 0
wu23399_ch03.qxd 12/13/06 17:38 Page 90
The expression 23 % 5 results in 3 because 23 divided by 5 is 4 with remainder 3.
Notice that x % y = 0 when y divides x perfectly; for example, 16 % 2 = 0. Also notice
that x % y = x when y is larger than x; for example, 23 % 25 = 23.
An operand in arithmetic expressions can be a constant, a variable, a method
call, or another arithmetic expression, possibly surrounded by parentheses. Let’s
look at examples. In the expression
x + 4
we have one addition operator and two operands—a variable x and a constant 4.
The addition operator is called a binary operator because it operates on two operands.
All other arithmetic operators except the minus are also binary. The minus and
plus operators can be both binary and unary. A unary operator operates on one
operand as in
–x
In the expression
x + 3 * y
the addition operator acts on operands x and 3 * y. The right operand for the addition
operator is itself an expression. Often a nested expression is called a subexpression.
The subexpression 3 * y has operands 3 and y. The following diagram illustrates this
relationship:
When two or more operators are present in an expression, we determine
the order of evaluation by following the precedence rules. For example, multi-
plication has a higher precedence than addition. Therefore, in the expression
x + 3 * y, the multiplication operation is evaluated first, and the addition operation
is evaluated next. Table 3.3 summarizes the precedence rules for arithmetic
operators.
y
3
x
3.2 Arithmetic Expressions 91
operand
binary operator
subexpression
precedence
rules
wu23399_ch03.qxd 12/13/06 17:38 Page 91
The following example illustrates the precedence rules applied to a complex
arithmetic expression:
92 Chapter 3 Numerical Data
Table
Table 3.3 Precedence rules for arithmetic operators and parentheses
Order Group Operator Rule
High Subexpression ( ) Subexpressions are evaluated first.If
parentheses are nested,the innermost
subexpression is evaluated first.If two or
more pairs of parentheses are on the same
level,then they are evaluated from left to
right.
Unary operator -, + Unary minuses and pluses are evaluated
second.
Multiplicative *, /, % Multiplicative operators are evaluated
operator third.If two or more multiplicative
operators are in an expression,then they
are evaluated from left to right.
Low Additive operator +, - Additive operators are evaluated last.If
two or more additive operators are in an
expression,then they are evaluated from
left to right.
a * (b + -(c / d) / e) * (f - g % h)
1 5
6
2
3
4
7
8
When an arithmetic expression consists of variables and constants of the same
data type, then the result of the expression will be that data type also. For example,
if the data type of a and b is int, then the result of the expression
a * b + 23
is also an int. When the data types of variables and constants in an arithmetic ex-
pression are different data types, then a casting conversion will take place. A casting
conversion, or typecasting, is a process that converts a value of one data type to an-
other data type. Two types of casting conversions in Java are implicit and explicit.
implicit and
explicit type-
casting
wu23399_ch03.qxd 12/13/06 17:38 Page 92
An implicit conversion called numeric promotion is applied to the operands of an
arithmetic operator. The promotion is based on the rules stated in Table 3.4. This
conversion is called promotion because the operand is converted from a lower to a
higher precision.
Instead of relying on implicit conversion, we can use explicit conversion to
convert an operand from one data type to another. Explicit conversion is applied to
an operand by using a typecast operator. For example, to convert the int variable x
in the expression
x / 3
to float so the result will not be truncated, we apply the typecast operator (float) as
(float) x / 3
The syntax is
( data type ) expression
The typecast operator is a unary operator and has a precedence higher than that of
any binary operator. You must use parentheses to typecast a subexpression; for ex-
ample, the expression
a + (double) (x + y * z)
will result in the subexpression x + y * z typecast to double.
Assuming the variable x is an int, then the assignment statement
x = 2 * (14343 / 2344);
will assign the integer result of the expression to the variable x. However, if the
data type of x is other than int, then an implicit conversion will occur so that the
3.2 Arithmetic Expressions 93
Table
Table 3.4 Rules for arithmetic promotion
Operator Type Promotion Rule
Unary 1. If the operand is of type byte or short,then it is
converted to int.
2. Otherwise,the operand remains the same type.
Binary 1. If either operand is of type double,then the other operand
is converted to double.
2. Otherwise,if either operand is of type float,then the other
operand is converted to float.
3. Otherwise,if either operand is of type long,then the other
operand is converted to long.
4. Otherwise,both operands are converted to int.
numeric
promotion
typecast
operator
typecasting
syntax
wu23399_ch03.qxd 12/13/06 17:38 Page 93
data type of the expression becomes the same as the data type of the variable. An
assignment conversion is another implicit conversion that occurs when the vari-
able and the value of an expression in an assignment statement are not of the
same data type. An assignment conversion occurs only if the data type of the
variable has a higher precision than the data type of the expression’s value. For
example,
double number;
number = 25;
is valid, but
int number;
number = 234.56; INVALID
is not.
In writing programs, we often have to increment or decrement the value of a
variable by a certain amount. For example, to increase the value of sum by 5, we
write
sum = sum + 5;
We can rewrite this statement witout repeating the same variable on the left- and
right-hand sides of the assignment symbol by using the shorthand assignment
operator:
sum += number;
Table 3.5 lists five shorthand assignment operators available in Java.
These shorthand assignment operators have precedence lower than that of any
other arithmetic operators; so, for example, the statement
sum *= a + b;
is equivalent to
sum = sum * (a + b);
94 Chapter 3 Numerical Data
assignment
conversion
shorthand
assignment
operator
Table
Table 3.5 Shorthand assignment operators
Operator Usage Meaning
+= a += b; a = a + b;
-= a -= b; a = a - b;
*= a *= b; a = a * b;
/= a /= b; a = a / b;
%= a %= b; a = a % b;
wu23399_ch03.qxd 12/13/06 17:38 Page 94
3.3 Constants 95
If we wish to assign a value to multiple variables, we can cascade the assignment
operations as
x = y = 1;
which is equivalent to saying
y = 1;
x = 1;
The assignment symbol = is actually an operator, and its precedence order is
lower than that of any other operators. Assignment operators are evaluated
right to left.
1. Evaluate the following expressions.
a. 3 + 5 / 7
b. 3 * 3 + 3 % 2
c. 3 + 2 / 5 + -2 * 4
d. 2 * (1 + -(3/4) / 2) * (2 - 6 % 3)
2. What is the data type of the result of the following expressions?
a. (3 + 5) / 7
b. (3 + 5) / (float) 7
c. (float) ( (3 + 5) / 7 )
3. Which of the following expressions is equivalent to b(c  34)(2a)?
a. -b * (c + 34) / 2 * a
b. -b * (c + 34) / (2 * a)
c. -b * c + 34 / (2 * a)
4. Rewrite the following statements without using the shorthand operators.
a. x += y;
b. x *= v + w;
c. x /= y;
3.3 Constants
While a program is running, different values may be assigned to a variable at dif-
ferent times (thus the name variable, since the values it contains can vary), but in
some cases we do not want this to happen. In other words, we want to “lock” the
assigned value so that no changes can take place. If we want a value to remain fixed,
then we use a constant. A constant is declared in a manner similar to a variable but
constant
wu23399_ch03.qxd 12/13/06 17:38 Page 95
with the additional reserved word final. A constant must be assigned a value at the
time of its declaration. Here’s an example of declaring four constants:
final double PI = 3.14159;
final short FARADAY_CONSTANT = 23060; // unit is cal/volt
final double CM_PER_INCH = 2.54;
final int MONTHS_IN_YEAR = 12;
We follow the standard Java convention to name a constant, using only capi-
tal letters and underscores. Judicious use of constants makes programs more read-
able. You will be seeing many uses of constants later in the book, beginning with the
sample program in this chapter.
The constant PI is called a named constant or symbolic constant. We refer to
symbolic constants with identifiers such as PI and FARADAY_CONSTANT. The sec-
ond type of constant is called a literal constant, and we refer to it by using an actual
value. For example, the following statements contain three literal constants:
final double PI = 3.14159 ;
double area;
area = 2 * PI * 345.79 ;
When we use the literal constant 2, the data type of the constant is set to int by
default. Then how can we specify a literal constant of type long?1
We append the
constant with an l (a lowercase letter L) or L as in
2L * PI * 345.79
How about the literal constant 345.79? Since the literal constant contains a
decimal point, its data type can be only float or double. But which one? The answer
is double. If a literal constant contains a decimal point, then it is of type double by
default. To designate a literal constant of type float, we must append the letter f or
F. For example,
2 * PI * 345.79F
To represent a double literal constant, we may optionally append a d or D. So
the following two constants are equivalent:
2 * PI * 345.79 is equivalent to 2 * PI * 345.79D
We also can express float and double literal constants in scientific notation as
Number  10exponent
96 Chapter 3 Numerical Data
named
constant
literal constant
Literal constants
1
In most cases, it is not significant to distinguish the two because of automatic type conversion; see Section 3.2.
wu23399_ch03.qxd 12/13/06 17:38 Page 96
which in Java is expressed as
number E exponent
3.4 Displaying Numerical Values 97
exponential
notation in
Java
Since a numerical constant such as 345.79 represents a double value, these
statements
float number;
number = 345.79;
for example, would result in a compilation error. The data types do not match,
and the variable (float) has lower precision than that of the constant (double).
To correct this error,we have to write the assignment statement as
number = 345.79f;
or
number = (float) 345.79;
This is one of the common errors that people make in writing Java programs, es-
pecially those with prior programming experience.
where number is a literal constant that may or may not contain a decimal point
and exponent is a signed or an unsigned integer. Lowercase e may be substituted
for the exponent symbol E. The whole expression may be suffixed by f, F, d, or D.
The number itself cannot be suffixed with symbols f, F, d, or D. Here are some
examples:
12.40e+209
23E33
29.0098e–102
234e+5D
4.45e2
Here are some additional examples of constant declarations:
final double SPEED_OF_LIGHT = 3.0E+10D; // unit is cm/s
final short MAX_WGT_ALLOWED = 400;
3.4 Displaying Numerical Values
In Chapter 2, we learned how to output string values to the console window
by using System.out. We can easily output numerical values to the console
window as well. We will use the same print and println methods to output
wu23399_ch03.qxd 1/11/07 11:49 Page 97
numerical values. Here’s a simple example that outputs the values of a constant
and a variable:
int num = 15;
System.out.print(num); //print a variable
System.out.print( ); //print a blank space
System.out.print(10); //print a constant
Executing the code will result in the following console window:
We can use the println method to skip a line after printing out the value.
Executing
int num = 15;
System.out.println(num);
System.out.println(10);
will result in
By using the concatenation operation, it is possible to output multiple values
with a single print or println method. For example, the statement
System.out.print(30 +   + 40);
is equivalent to
System.out.print(30);
System.out.print( );
System.out.print(40);
Notice that the expression
30 +   + 40
mixes numerical values and a string. We learned in Chapter 2 that the plus symbol
is used to concatenate strings, for example,
Benjamin +   + Franklin
98 Chapter 3 Numerical Data
15 10
Console
Window
15
10
Console
Window
wu23399_ch03.qxd 12/13/06 17:38 Page 98
And, in this chapter, we learned the same plus symbol is used to add numerical
values, for example,
4 + 36
The plus symbol, therefore, could mean two different things: string concatenation
or numerical addition. When a symbol is used to represent more than one operation,
this is called operator overloading.
What happens when the plus symbol appears in a mixed expression? When the
Java compiler encounters an overloaded operator, the compiler determines the mean-
ing of a symbol by its context. If the left operand and the right operand of the plus
symbol are both numerical values, then the compiler will treat the symbol as addition;
otherwise, it will treat the symbol as concatenation.The plus symbol operator is eval-
uated from left to right, and the result of concatenation is a string, so the code
int x = 1;
int y = 2;
String output = test + x + y;
will result in output being set to
test12
while the statement
String output = x + y + test;
will result in output being set to
3test
To get the result of test3, we have to write the statement as
String output = test + (x + y);
so the arithmetic addition is performed first.
Now let’s look at a small sample program that illustrates a typical use of
string concatenation in displaying computation results. In this sample program, we
compute the circumference and the area of a circle with a given radius. The value
for the radius is assigned in the program (we will discuss how to input this value in
Section 3.5). Here’s the program:
3.4 Displaying Numerical Values 99
operator
overloading
/*
Chapter 3 Sample Program: Compute Area and Circumference
File: Ch3Circle.java
*/
test 1
test1
test12
2
1 2
3
3test
test
(add)
wu23399_ch03.qxd 12/13/06 17:38 Page 99
When we run this program, we get the following output:
Notice the precision of decimal places displayed for the results, especially the one
for the circumference. Although we desire such a high level of precision provided
by double values during the computation, we may not when displaying the result.
We can restrict the number of decimal places to display by using the DecimalFormat
class from the java.text package.
Although the full use of the DecimalFormat class can be fairly complicated,
it is very straightforward if all we want is to limit the number of decimal places
to be displayed. To limit the decimal places to three, for example, we create a
DecimalFormat object as
DecimalFormat df = new DecimalFormat(0.000);
and use it to format the number as
double num = 234.5698709;
System.out.println(Num:  + df.format(num));
When we add an instance of the DecimalFormat class named df and change
the output statement of the Ch3Circle class to
System.out.println(Given Radius:  + df.format(radius));
System.out.println(Area:  + df.format(area));
System.out.println(Circumference: 
+ df.format(circumference));
100 Chapter 3 Numerical Data
class Ch3Circle {
public static void main(String[] args) {
final double PI = 3.14159;
double radius, area, circumference;
radius = 2.35;
//compute the area and circumference
area = PI * radius * radius;
circumference = 2.0 * PI * radius;
System.out.println(Given Radius:  + radius);
System.out.println(Area:  + area);
System.out.println(Circumference:  + circumference);
}
}
Given Radius: 2.35
Area: 17.349430775000002
Circumference: 14.765473
Console
Window
wu23399_ch03.qxd 12/13/06 17:38 Page 100
we produce the following result:
The modified class is named Ch3Circle2 (not shown here).
Instead of using one println method per line of output, it is possible to output
multiple lines with a single println or print method by embedding a new-line control
character in the output. We briefly mentioned a control character in Section 2.4.4. A
control character is for controlling the output, and we use the backslash symbol to
denote a control character. The new-line control character is denoted as n and has the
effect of pressing the Enter key in the output. For example, the statements
System.out.println(Given Radius:  + radius);
System.out.println(Area:  + area);
System.out.println(Circumference:  + circumference);
can be written by using only one println statement as
System.out.println(Given Radius:  + radius + n +
Area:  + area + n +
Circumference:  + circumference);
There is no limit to the number of new-line control characters you can embed, so we
can easily skip two lines, for example, by putting two new-line control characters as
follows:
System.out.println(Number 1:  + num1 + nn +
Number 2:  + num2);
Another useful control character is a tab, which is denoted as t. We can use
the tab control character to output the labels, and this results in two columns as
follows:
System.out.println(Given Radius:  + t + radius + n +
Area:  + tt + area + n +
Circumference:  + t + circumference);
Notice there are two tabs before we output the area. You need to experiment with
the actual number of tabs to get the right output (the actual number of spaces used
for each tab could be different depending on your Java IDE). The resulting output
will be
3.4 Displaying Numerical Values 101
Given Radius: 2.350
Area: 17.349
Circumference: 14.765
Console
Window
new-line
control
character
tab
control
character
Given Radius: 2.35
Area: 17.349430775000002
Circumference: 14.765473
Console
Window
wu23399_ch03.qxd 12/13/06 17:38 Page 101
You can also adjust the output format by appending blank spaces in the label.
For example, you can rewrite the sample statement as
System.out.println(Given Radius:  + t + radius + n +
Area:  + t + area + n +
Circumference:  + t + circumference);
And, as always, the use of symbolic constants will clean up the code:
...
final String TAB = t;
final String NEWLINE = n;
...
System.out.println(
Given Radius:  + TAB + radius + NEWLINE +
Area:  + TAB + area + NEWLINE +
Circumference:  + TAB + circumference);
The new program that illustrates the use of both DecimalFormat and control char-
acters is named Ch3Circle3. Here’s the program:
102 Chapter 3 Numerical Data
/*
Chapter 3 Sample Program: Compute Area and Circumference
File: Ch3Circle3.java
*/
import java.text.*;
class Ch3Circle3 {
public static void main(String[] args) {
final double PI = 3.14159;
final String TAB = t;
final String NEWLINE = n;
double radius, area, circumference;
DecimalFormat df = new DecimalFormat(0.000);
radius = 2.35;
//compute the area and circumference
area = PI * radius * radius;
circumference = 2.0 * PI * radius;
//Display the results
System.out.println(
Given Radius:  + TAB + df.format(radius) + NEWLINE +
wu23399_ch03.qxd 12/13/06 17:38 Page 102
3.5 Getting Numerical Input 103
1. What is the purpose of the control characters?
2. Which control character is used for a new line?
3. Using one print statement, output the following:
Hello, world!
My favorite Ben Franklin quote:
An investment in knowledge
always pays the best interest.
3.5 Getting Numerical Input
We learned how to input string values by using the Scanner class in Chapter 2. We
study how to input numerical values with the Scanner class in this section. To input
strings, we use the next method of the Scanner class. For the numerical input values,
we use an equivalent method that corresponds to the data type of the value we try
to input. For instance, to input an int value, we use the nextInt method. Here’s an
example of inputting a person’s age:
Scanner scanner = new Scanner(System.in);
int age;
System.out.print(Enter your age: );
age = scanner.nextInt( );
In addition to the int data type, we have five input methods that correspond to
the other numerical data types. The six input methods for the primitive numerical
data types are listed in Table 3.6.
Table
Table 3.6 Methods to input six numerical data types
Method Example
nextByte( ) byte b = scanner.nextByte( );
nextDouble( ) double d = scanner.nextDouble( );
nextFloat( ) float f = scanner.nextFloat( );
nextInt( ) int i = scanner.nextInt( );
nextLong( ) long l = scanner.nextLong( );
nextShort( ) short s = scanner.nextShort( );
Area:  + TAB + df.format(area) + NEWLINE +
Circumference:  + TAB + df.format(circumference));
}
}
wu23399_ch03.qxd 12/13/06 17:38 Page 103
ENTER
ENTER
Enter two integers: 12
87
num1 = 12 and num2 = 87
Since the new-line character (when we press the Enter key, this new-line char-
acter is entered into the system) is also treated as white space, we can enter the two
integers by pressing the Enter key after each number. Here’s a sample:
104 Chapter 3 Numerical Data
The following example inputs a person’s height in inches (int) and GPA (float):
Scanner scanner = new Scanner(System.in);
int height;
float gpa;
System.out.print(Enter your height in inches: );
height = scanner.nextInt( );
System.out.print(Enter your gpa: );
gpa = scanner.nextFloat( );
Remember that the default delimiter between the input values is a white space
(such as the blank space or a tab); it is possible to input more than one value on a
single line. The following code inputs two integers:
Scanner scanner = new Scanner(System.in);
int num1, num2;
System.out.print(Enter two integers: );
num1 = scanner.nextInt( );
num2 = scanner.nextInt( );
System.out.print(num1 =  + num1 +  num2 =  + num2);
And here’s a sample interaction:
ENTER
When we enter data using System.in, they are placed in input buffer. And the
next available data in the input buffer are processed when one of the input methods
is called. This means that the actual processing of input data does not necessarily
correspond to the display timing of the prompts. Let’s look at an example. Consider
the following code:
Scanner scanner = new Scanner(System.in);
int num1, num2, num3;
Space separates the
two input values.
input buffer
Enter two integers: 12 8
num1 = 12 and num2 = 87
wu23399_ch03.qxd 12/13/06 17:38 Page 104
3.5 Getting Numerical Input 105
System.out.print(Enter Number 1: );
num1 = scanner.nextInt( );
System.out.print(Enter Number 2: );
num2 = scanner.nextInt( );
System.out.print(Enter Number 3: );
num3 = scanner.nextInt( );
System.out.print(Values entered are  +
num1 +   + num2 +   + num3);
We expect the majority of users will input three integers, one at a time, as requested
by the prompts:
Enter Number 1: 10
Enter Number 2: 20
Enter Number 3: 30
Values entered are 10 20 30
However, users do not really have to enter the values one at a time. It is possible
to enter all three values on a single line without waiting for prompts, for example. This
will result in an awkward display in the console window. Here’s an example:
Enter Number 1: 10, 20, 30
Enter Number 2: Enter Number 3: Values entered are 10 20 30
Although the display is awkward, the input values are assigned to the respec-
tive variables correctly. This is so because the three input values are placed in the
input buffer, and when the second and third nextInt methods are called, the corre-
sponding values are in the input buffer, so there’s no problem inputting them.
In Section 3.2, we explained the assignment conversion that allows us to
assign a value to a higher-precision variable (e.g., assigning an int value to a dou-
ble variable). This type of implicit conversion also occurs with the Scanner class.
For example, the nextDouble method works without a problem as long as the user
enters a value that is assignable to a double variable. Here’s an example:
Scanner scanner = new Scanner(System.in);
double num;
System.out.print(Enter a double: );
num = scanner.nextDouble( );
System.out.print(You entered  + num);
Enter a double: 35
You entered 35.0
ENTER
ENTER
ENTER
ENTER
ENTER
wu23399_ch03.qxd 12/13/06 17:38 Page 105
Bad Version
ENTER
ENTER
ENTER
Everything seems to be working okay. What will happen if the name of a horse has
more than one word, such as Sea Biscuit? The code will not work because only the
first word is assigned to the String variable horseName. Remember that the default
delimiter is the white space, so the blank space after the first word is treated as the
end of the first input. Here’s the result when you enter Sea Biscuit:
106 Chapter 3 Numerical Data
Enter the horse name: Sea Biscuit
Enter the age: java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:819)
at java.util.Scanner.next(Scanner.java:1431)
at java.util.Scanner.nextInt(Scanner.java:2040)
...
Only the first four lines of error
messages are shown here.
The nextDouble method accepts the value 35 and then converts it to a double data
type. The method returns a double value, so even if the user enters an integer, you
cannot assign the input to an int variable. The following code is therefore invalid:
Scanner scanner = new Scanner(System.in);
int num;
System.out.print(Enter an integer: );
num = scanner.nextDouble( ); Type mismatch
System.out.print(You entered  + num);
Now let’s study how we can mix the input of strings and numerical values. We
begin with an example. Consider inputting a racehorse’s name and age. Here are a
proposed code and a sample of expected interaction:
Scanner scanner = new Scanner(System.in);
String horseName;
int age;
System.out.print(Enter the horse name: );
horseName = scanner.next( );
System.out.print(Enter the age: );
age = scanner.nextInt( );
System.out.print(horseName +  is  + age + years old. );
Enter the horse name: Barbaro
Enter the age: 3
Barbaro is 3 years old.
wu23399_ch03.qxd 12/13/06 17:38 Page 106
ENTER
ENTER
3.5 Getting Numerical Input 107
To input more than one string and primitive numerical data,set the line separator
as the delimiter and input one value per input line.
The most reasonable solution here is to change the delimiter to the line sepa-
rator, as described in Section 2.4.4. Here’s how:
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
//the rest is the same
Enter the horse name: Sea Biscuit
Enter the age: 3
Sea Biscuit is 3 years old.
For most situations, using the line separator as the delimiter and inputting one
value per input line are the best approach. We can, however, use any string for the
delimiter. So, for example, we can delimit the input values with a character such as
the pound sign (#), provided, of course, that the pound sign does not occur in the
actual input values.
Instead of using the data type specific methods such as nextInt, nextDouble,
and others of the Scanner class, we can input a numerical value in a string format
and convert it to an appropriate data type by ourselves. For example, we can use the
class method parseInt of the Integer class to convert a string to an int. Here’s a state-
ment that converts 14 to an int value 14:
int num = Integer.parseInt(14);
So, the statement
int num = Integer.parseInt(scanner.next( ));
is equivalent to
int num = scanner.nextInt( );
Passing a string that cannot be converted to an int (e.g., 12b) will result in
an error. The conversion method is not particularly useful or necessary with the
scanner, but it can be when the input source is different from the scanner. Other
common conversion methods are parseDouble, parseFloat, and parseLong of the
Double, Float, and Long classes, respectively.
wu23399_ch03.qxd 12/13/06 17:38 Page 107
108 Chapter 3 Numerical Data
/*
Chapter 3 Sample Program: Compute Area and Circumference with
formatting and standard I/O
File: Ch3Circle4.java
*/
import java.text.*;
import java.util.*;
class Ch3Circle4 {
public static void main(String[] args) {
final double PI = 3.14159;
final String TAB = t;
final String NEWLINE = n;
double radius, area, circumference;
Scanner scanner = new Scanner(System.in);
DecimalFormat df = new DecimalFormat(0.000);
System.out.println(Enter radius: );
radius = scanner.nextDouble( );
//compute the area and circumference
area = PI * radius * radius;
circumference = 2.0 * PI * radius;
//Display the results
System.out.println(
Given Radius:  + TAB + df.format(radius) + NEWLINE +
Area:  + TAB + df.format(area) + NEWLINE +
Circumference:  + TAB + df.format(circumference));
}
}
1. Write a code to input the height of a user in feet (int) and inches (int).
2. Write a code to input the full name of a person and his or her age. The full
name of a person includes the first name and the last name.
3. Write a code that creates a Scanner object and sets its delimiter to the pound sign.
We close this section by presenting a sample program that extends the
Ch3Circle3 class by accepting the radius of a circle as an input. Here’s the program:
wu23399_ch03.qxd 12/13/06 17:38 Page 108
3.6 The Math Class
Using only the arithmetic operators to express numerical computations is very lim-
iting. Many computations require the use of mathematical functions. For example,
to express the mathematical formula

1
2
 sin x  


y


we need the trigonometric sine and square root functions. The Math class in the
java.lang package contains class methods for commonly used mathematical func-
tions. Table 3.7 is a partial list of class methods available in the Math class. The class
also has two class constants PI and E for  and the natural number e, respectively.
Using the Math class constant and methods, we can express the preceding formula as
(1.0 /2.0) * Math.sin( x - Math.PI / Math.sqrt(y) )
3.6 The Math Class 109
Table
Table 3.7 Math class methods for commonly used mathematical functions
Class Argument Result
Method Type Type Description Example
abs( a ) int int Returns the absolute int abs(10) → 10
value of a. abs(5) → 5
long long Returns the absolute
long value of a.
float float Returns the absolute
float value of a.
double double Returns the absolute
double value of a.
acos( a )†
double double Returns the arccosine acos(1)
of a. → 3.14159
asin( a )†
double double Returns the arcsine asin(1)
of a. → 1.57079
atan( a )†
double double Returns the arctangent atan(1)
of a. → 0.785398
ceil( a ) double double Returns the smallest ceil(5.6) → 6.0
whole number greater ceil(5.0) → 5.0
than or equal to a. ceil(5.6)
→ 5.0
cos( a )†
double double Returns the trigonometric cos(2) → 0.0
cosine of a.
exp( a ) double double Returns the natural exp(2)
number e (2.718 ...) → 7.389056099
raised to the power of a.
wu23399_ch03.qxd 12/13/06 17:38 Page 109
110 Chapter 3 Numerical Data
Table
Table 3.7 Math class methods for commonly used mathematical functions (Continued)
Class Argument Result
Method Type Type Description Example
floor( a ) double double Returns the largest floor(5.6) → 5.0
whole number less than floor(5.0) → 5.0
or equal to a. floor(5.6)
→ 6.0
log( a ) double double Returns the natural log(2.7183)
logarithm (base e) of a. → 1.0
max( a, b ) int int Returns the larger of a max(10,20)
and b. → 20
long long Same as above.
float float Same as above.
min(a, b) int int Returns the smaller of a min(10,20)
and b. → 10
long long Same as above.
float float Same as above.
pow(a, b) double double Returns the number a pow( 2.0,3.0)
raised to the power of b. → 8.0
random( ) none double Generates a random Examples given
number greater than or in Chapter 5
equal to 0.0 and
less than 1.0.
round( a ) float int Returns the int value of round(5.6) → 6
a rounded to the round(5.4) → 5
nearest whole number. round(5.6)
→ 6
double long Returns the float value of
a rounded to the
nearest whole number.
sin( a )†
double double Returns the sin(2 )
trigonometric sine of a. → 1.0
sqrt( a ) double double Returns the square root sqrt(9.0) → 3.0
of a.
tan( a )†
double double Returns the trigono- tan(4)
metric tangent of a. → 1.0
toDegrees double double Converts the given toDegrees(4)
angle in radians to → 45.0
degrees.
toRadians double double Reverse of toDegrees. toRadians(90.0)
→ 1.5707963
†
All trigonometric functions are computed in radians.
wu23399_ch03.qxd 12/13/06 17:38 Page 110
Notice how the class methods and class constants are referred to in the ex-
pression. The syntax is
class name . method name ( arguments )
or
class name . class constant
Let’s conclude this section with a sample program. Today is the final meet of
the women’s rowing team against the arch rival university before the upcoming
Division I NCAA championship. The cheerleaders of the rival team hoisted their
school flag on the other shore of the river to boost their moral. Not to be outdone,
we want to hoist our school flag, too. To bring the Goddess of Victory to our side,
we want our pole to be taller than theirs. Since they won’t let us, we can’t find the
height of their pole by actually measuring it. We can, however, determine the height
without actually measuring it if we know the distance b to their flagpole. We can use
the tangent of angle to determine the pole’s height h as follows:
Unfortunately, there’s no means for us to go across the river to find out the dis-
tance b. After a moment of deep meditation, it hits us that there’s no need to go
across the river. We can determine the pole’s height by measuring angles from two
points on this side of the riverbank, as shown below:
h
d
A
B


h
h  b · tan
b

3.6 The Math Class 111
wu23399_ch03.qxd 12/13/06 17:38 Page 111
And the equation to compute the height h is
h 
Once we have this equation, all that’s left is to put together a Java program. Here’s
the program:
d sin  sin 

sin( 
 ) sin
(  
)

112 Chapter 3 Numerical Data
/*
Chapter 3 Sample Program: Estimate the Pole Height
File: Ch3PoleHeight.java
*/
import java.text.*;
import java.util.*;
class Ch3PoleHeight {
public static void main( String[] args ) {
double height; //height of the pole
double distance; //distance between points A and B
double alpha; //angle measured at point A
double beta; //angle measured at point B
double alphaRad; //angle alpha in radians
double betaRad; //angle beta in radians
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
//Get three input values
System.out.print(Angle alpha (in degrees):);
alpha = scanner.nextDouble();
System.out.print(Angle beta (in degree):);
beta = scanner.nextDouble();
System.out.print(Distance between points A and B (ft):);
distance = scanner.nextDouble();
//compute the height of the tower
alphaRad = Math.toRadians(alpha);
betaRad = Math.toRadians(beta);
height = ( distance * Math.sin(alphaRad) * Math.sin(betaRad) )
/
Math.sqrt( Math.sin(alphaRad + betaRad) *
Math.sin(alphaRad - betaRad) );
wu23399_ch03.qxd 12/13/06 17:38 Page 112
3.7 Random Number Generation 113
DecimalFormat df = new DecimalFormat(0.000);
System.out.println(lnln Estimating the height of the pole
+ nn
+ Angle at point A (deg):  + df.format(alpha) + n
+ Angle at point B (deg):  + df.format(beta) + n
+ Distance between A and B (ft):  + df.format(distance)+ n
+ Estimated height (ft):  + df.format(height));
}
}
1. What’s wrong with the following?
a. y = (1/2) * Math.sqrt( X );
b. y = sqrt(38.0);
c. y = Math.exp(2, 3);
d. y = math.sqrt( b*b - 4*a*c) / ( 2 * a );
2. If another programmer writes the following statements, do you suspect any
misunderstanding on the part of this programmer? What will be the value of y?
a. y = Math.sin( 360 ) ;
b. y = Math.cos( 45 );
3.7 Random Number Generation
In many computer applications, especially in simulation and games, we need to gen-
erate random numbers. For example, to simulate a roll of dice, we can generate an
integer between 1 and 6. In this section, we explain how to generate random numbers
using the random method of the Math class. (Alternatively, you can use the Random
class. We refer you to the JavaAPI documentation for information on this class.)
The method random is called a pseudorandom number generator and returns
a number (type double) that is greater than or equal to 0.0 but less than 1.0, that is,
0.0  X  1.0. The generated number is called a pseudorandom number because
the number is not truly random. When we call this method repeatedly, eventually the
numbers generated will repeat themselves. Therefore, theoretically the generated
numbers are not random; but for all practical purposes, they are random enough.
The random numbers we want to generate for most applications are integers.
For example, to simulate the draw of a card, we need to generate an integer between
1 and 4 for the suit and an integer between 1 and 13 for the number. Since the number
returned from the random method ranges from 0.0 up to but not including 1.0, we
need to perform some form of conversion so the converted number will fall in our de-
sired range. Let’s assume the range of integer values we want is [min, max]. If X is a
pseudorandom
number
generator
wu23399_ch03.qxd 12/13/06 17:38 Page 113
number returned by random, then we can convert it into a number Y such that Y is in
the range [min, max] that is, min  Y  max by applying the following fourmula:
Y  X  (max  min  1)  min
For many applications, the value for min is 1, so the formula is simplified to
Y  X  max  1
Expressing the general formula in Java will result in the following statement:
//assume correct values are assigned to 'max' and 'min'
int randomNumber
= (int) (Math.floor(Math.random() * ( max-min+1))
+ min);
Notice that we have to typecast the result of Math.floor to int because the data
type of the result is double.
Let’s write a short program that selects a winner among the party goers of the
annual spring fraternity dance. The party goers will receive numbers M  1, M  2,
M  3, and so on, as they enter the house. The starting value M is selected by the
chairperson of the party committee. The last number assigned is M  N if there are
N party goers. At the end of the party, we run the program that will randomly select
the winning number from the range of M  1 and M  N. Here’s the program:
114 Chapter 3 Numerical Data
/*
Chapter 3 Sample Program: Select the Winning Number
File: Ch3SelectWinner.java
*/
import java.util.*;
class Ch3SelectWinner {
public static void main(String[] args) {
int startingNumber; //the starting number
int count; //the number of party goers
int winningNumber; //the winner
int min, max; //the range of random numbers to generate
Scanner scan = new Scanner(System.in);
//Get two input values
System.out.print(Enter the starting number M: );
startingNumber = scan.nextInt();
System.out.print(Enter the number of party goers: );
count = scan.nextInt();
wu23399_ch03.qxd 1/11/07 11:49 Page 114
3.8 The GregorianCalendar Class
In Chapter 2, we introduced the java.util.Date class to represent a specific instant in
time. Notice that we are using here the more concise expression “the java.util.Date
class” to refer to a class from a specific package instead of the longer expression “the
Date class from the java.util package.” This shorter version is our preferred way of
notation when we need or want to identify the package to which the class belongs.
3.8 The GregorianCalendar Class 115
//select the winner
min = startingNumber + 1;
max = startingNumber + count;
winningNumber = (int) ( Math.floor(Math.random() * (max - min + 1))
+ min);
System.out.println(nThe Winning Number is  + winningNumber);
}
}
When we need to identify the specific package to which a class belongs,we will
commonly use the concise expression with the full path name,such as
java.util.Date, instead of writing“the Date class from the java.util package.”
In addition to this class, we have a very useful class named java.util.Gregorian-
Calendar in manipulating calendar information such as year, month, and day. We can
create a new GregorianCalendar object that represents today as
GregorianCalendar today = new GregorianCalendar( );
or a specific day, say, July 4, 1776, by passing year, month, and day as the parame-
ters as
GregorianCalendar independenceDay =
new GregorianCalendar(1776, 6, 4);
No, the value of 6 as the second parameter is not an error. The first month of a
year, January, is represented by 0, the second month by 1, and so forth. To avoid
confusion, we can use constants defined for months in the superclass Calendar
(GregorianCalendar is a subclass of Calendar). Instead of remembering that the
The value of 6
means July.
Gregorian-
Calendar
wu23399_ch03.qxd 12/13/06 17:38 Page 115
value 6 represents July, we can use the defined constant Calendar.JULY as
GregorianCalendar independenceDay =
new GregorianCalendar(1776, Calendar.JULY, 4);
Table 3.8 explains the use of some of the more common constants defined in the
Calendar class.
When the date and time are November 11, 2002, 6:13 p.m. and we run the
Ch3TestCalendar program, we will see the result shown in Figure 3.4.
116 Chapter 3 Numerical Data
Table
Table 3.8
Constant Description
YEAR The year portion of the calendar date
MONTH The month portion of the calendar date
DATE The day of the month
DAY_OF_MONTH Same as DATE
DAY_OF_YEAR The day number within the year
DAY_OF_MONTH The day number within the month
DAY_OF_WEEK The day of the week (Sun—1,Mon—2,etc.)
WEEK_OF_YEAR The week number within the year
WEEK_OF_MONTH The week number within the month
AM_PM The indicator for AM or PM (AM—0 and PM—1)
HOUR The hour in 12-hour notation
HOUR_OF_DAY The hour in 24-hour notation
MINUTE The minute within the hour
Constants defined in the Calendar class for retrieved different pieces of
calendar/time information
Figure 3.4 Result of running the Ch3TestCalender program at November 11,2002,6:13 p.m.
wu23399_ch03.qxd 12/13/06 17:38 Page 116
Notice that the first line in the output shows the full date and time information.
The full date and time information can be accessed by calling the calendar object’s
getTime method. This method returns the same information as a Date object.
Notice also that we get only the numerical values when we retrieve the day
of the week or month information. We can spell out the information by using the
SimpleDateFormat class. Since the constructor of the SimpleDateFormat class ac-
cepts only the Date object, first we need to convert a GregorianCalendar object to
an equivalent Date object by calling its getTime method. For example, here’s how
3.8 The GregorianCalendar Class 117
/*
Chapter 3 Sample Program: Display Calendar Info
File: Ch3TestCalendar.java
*/
import java.util.*;
class Ch3TestCalendar {
public static void main(String[] args) {
GregorianCalendar cal = new GregorianCalendar();
System.out.println(cal.getTime());
System.out.println();
System.out.println(YEAR:  + cal.get(Calendar.YEAR));
System.out.println(MONTH:  + cal.get(Calendar.MONTH));
System.out.println(DATE:  + cal.get(Calendar.DATE));
System.out.println(DAY_OF_YEAR: 
+ cal.get(Calendar.DAY_OF_YEAR));
System.out.println(DAY_OF_MONTH: 
+ cal.get(Calendar.DAY_OF_MONTH));
System.out.println(DAY_OF_WEEK: 
+ cal.get(Calendar.DAY_OF_WEEK));
System.out.println(WEEK_OF_YEAR: 
+ cal.get(Calendar.WEEK_OF_YEAR));
System.out.println(WEEK_OF_MONTH: 
+ cal.get(Calendar.WEEK_OF_MONTH));
System.out.println(AM_PM:  + cal.get(Calendar.AM_PM));
System.out.println(HOUR:  + cal.get(Calendar.HOUR));
System.out.println(HOUR_OF_DAY: 
+ cal.get(Calendar.HOUR_OF_DAY));
System.out.println(MINUTE:  + cal.get(Calendar.MINUTE));
}
}
getTime
wu23399_ch03.qxd 12/13/06 17:38 Page 117
we can display the day of the week on which our Declaration of Independence was
adopted in Philadelphia:
118 Chapter 3 Numerical Data
/*
Chapter 3 Sample Program: Day of the week the Declaration of
Independence was adopted
File: Ch3IndependenceDay.java
*/
import java.util.*;
import java.text.*;
class Ch3IndependenceDay {
public static void main(String[] args) {
GregorianCalendar independenceDay
= new GregorianCalendar(1776, Calendar.JULY, 4);
SimpleDateFormat sdf = new SimpleDateFormat(EEEE);
System.out.println(It was adopted on 
+ sdf.format(independenceDay.getTime()));
}
}
Let’s finish the section with a sample program that extends the Ch3Indepen-
denceDay program. We will allow the user to enter the year, month, and day; and we
will reply with the day of the week of the given date (our birthday, grandparent’s
wedding day, and so on). Here’s the program:
/*
Chapter 3 Sample Program: Find the Day of Week of a Given Date
File: Ch3FindDayOfWeek.java
*/
import java.util.*;
import java.text.*;
class Ch3FindDayOfWeek {
public static void main(String[] args) {
int year, month, day;
GregorianCalendar cal;
SimpleDateFormat sdf;
wu23399_ch03.qxd 12/13/06 17:38 Page 118
Notice that we are allowing the user to enter the month as an integer be-
tween 1 and 12, so we need to subtract 1 from the entered data in creating a new
GregorianCalendar object.
3.8 The GregorianCalendar Class 119
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
System.out.print(Year (yyyy): );
year = scanner.nextInt();
System.out.print(Month (1-12): );
month = scanner.nextInt();
System.out.print(Day (1-31): );
day = scanner.nextInt();
cal = new GregorianCalendar(year, month-1, day);
sdf = new SimpleDateFormat(EEEE);
System.out.println();
System.out.println(Day of Week:  + sdf.format(cal.getTime()));
}
}
The Gregorian calendar system was adopted by England and its colonies, including the
colonial United States, in 1752. So the technique shown here works only after this
adoption. For a fascinating story about calendars, visit
http://webexhibits.org/calendars/year-countries.html
Running Ch3IndpendenceDay will tell you that our venerable document was signed
on Thursday. History textbooks will say something like “the document was formally
adopted July 4, 1776, on a bright, but cool Philadelphia day” but never the day of the
week. Well, now you know. See how useful Java is? By the way, the document was
adopted by the Second Continental Congress on July 4, but the actual signing did
not take place until August 2 (it was Friday—what a great reason for a TGIF party)
after the approval of all 13 colonies. For more stories behind the Declaration of
Independence, visit
http://www.ushistory.org/declaration/
wu23399_ch03.qxd 12/13/06 17:38 Page 119
Loan Calculator
In this section,we develop a simple loan calculator program.We develop this program by
using an incremental development technique, which develops the program in small in-
cremental steps.We start out with a bare-bones program and gradually build up the pro-
gram by adding more and more code to it.At each incremental step,we design,code,and
test the program before moving on to the next step. This methodical development of a
program allows us to focus our attention on a single task at each step, and this reduces
the chance of introducing errors into the program.
Problem Statement
The next time you buy a new TV or a stereo, watch out for those “0% down, 0% interest
until next July”deals. Read the fine print, and you’ll notice that if you don’t make the full
payment by the end of a certain date,hefty interest will start accruing.You may be better
off to get an ordinary loan from the beginning with a cheaper interest rate.What matters
most is the total payment (loan amount plus total interest) you’ll have to make.To com-
pare different loan deals, let’s develop a loan calculator. Here’s the problem statement:
Write a loan calculator program that computes both monthly and total
payments for a given loan amount,annual interest rate,and loan period.
Overall Plan
Our first task is to map out the overall plan for development.We will identify classes nec-
essary for the program and the steps we will follow to implement the program.We begin
with the outline of program logic.For a simple program such as this one,it is kind of obvi-
ous;but to practice the incremental development,let’s put down the outline of program
flow explicitly.We can express the program flow as having three tasks:
1. Get three input values:loanAmount, interestRate, and loanPeriod.
2. Compute the monthly and total payments.
3. Output the results.
Having identified the three major tasks of the program,we now identify the classes
we can use to implement the three tasks. For input and output, we continue to use the
Scanner class and System.out (PrintStream). For computing the monthly and total
payments, there are no standard classes that will provide such computation, so we have
to write our own code.
The formula for computing the monthly payment can be found in any mathemat-
ics book that covers geometric sequences.It is
Monthly payment 
L  R

1  [1(1  R)]N
Sample Development
3.9 Sample Development
120 Chapter 3 Numerical Data
program
tasks
wu23399_ch03.qxd 12/13/06 17:38 Page 120
where L is the loan amount,R is the monthly interest rate,and N is the number of payments.
ThemonthlyrateRisexpressedinafractionalvalue,for example,0.01for 1 percent monthly
rate.Once the monthly payment is derived,the total payment can be determined by multi-
plying the monthly payment by the number of months the payment is made.Since the for-
mula includes exponentiation,we will have to use the pow method of the Math class.
Let’s summarize what we have decided so far in a design document:
3.9 Sample Development 121
program
classes
Design Document: LoanCalculator
Class Purpose
LoanCalculator The main class of the program.
Scanner The class is used to get three input values:loan amount,
annual interest rate,and loan period.
PrintStream System.out is used to display the input values and two
(System.out) computed results:monthly payment and total payment.
Math The pow method is used to evaluate exponentiation in the
formula for computing the monthly payment.This class is
from java.lang. Note: You don’t have to import
java.lang.The classes in java.lang are available to a
program without importing.
The program diagram based on the classes listed in the design document is shown
in Figure 3.5. Keep in mind that this is only a preliminary design. The preliminary docu-
ment is really a working document that we will modify and expand as we progress
through the development steps.
Before we can actually start our development, we must sketch the steps we will
follow to implement the program.There is more than one possible sequence of steps to
implement a program,and the number of possible sequences will increase as the program
becomes more complex. For this program, we will implement the program in four steps:
1. Start with code to accept three input values.
2. Add code to output the results.
3. Add code to compute the monthly and total payments.
4. Update or modify code and tie up any loose ends.
Notice how the first three steps are ordered. Other orders are possible to develop
this program.So why did we choose this particular order?The main reason is our desire to
defer the most difficult task until the end.It’s possible,but if we implement the computa-
tion part in the second incremental step, then we need to code some temporary output
routines to verify that the computation is done correctly. However, if we implement the
real output routines before implementing the computation routines, then there is no
develop-
ment steps
wu23399_ch03.qxd 12/13/06 17:38 Page 121
122 Chapter 3 Numerical Data
need for us to worry about temporary output routines.As for step 1 and step 2,their rela-
tive order does not matter much.We simply chose to implement the input routine before
the output routine because input comes before output in the program.
Step 1 Development: Input Three Data Values
The next task is to determine how we will accept the input values.The problem statement
does not specify the exact format of input,so we will decide that now.Based on how peo-
ple normally refer to loans,the input values will be accepted in the following format:
Input Format Data Type
Loan amount In dollars and cents (for example,15000.00) double
Annual interest rate In percent (for example,12.5) double
Loan period In years (for example,30) int
Be aware that we need to convert the annual interest rate to the monthly interest rate and
the input value loan period to the number of monthly payments, to use the given
formula. In this case, the conversion is very simple, but even if the conversion routines
were more complicated, we must do the conversion. It is not acceptable to ask users to
step 1
design
3.9 Sample Development—continued
Figure 3.5 The object diagram for the program LoanCalculator.
LoanCalculator
Scanner
Math
System.out : PrintStream
wu23399_ch03.qxd 12/13/06 17:38 Page 122
3.9 Sample Development 123
enter an input value that is unnatural to them.For example, people do not think of inter-
est rates in fractional values such as 0.07. They think of interest in terms of percentages
such as 7 percent. Computer programs work for humans, not the other way round.
Programs we develop should not support an interface that is difficult and awkward for
humans to use.
When the user inputs an invalid value, for example, an input string value that can-
not be converted to a numerical value or that converts to a negative number,the program
should respond accordingly, such as by printing an error message. We do not possess
enough skills to implement such a robust program yet, so we will make the following
assumptions: (1) The input values are nonnegative numbers, and (2) the loan period is a
whole number.
One important objective of this step is to verify that the input values are read
in correctly by the program. To verify this, we will echo-print the input values to
System.out.
Here’s our step 1 program:
step 1 code
/*
Chapter 3 Sample Development: Loan Calculator (Step 1)
File: Step1/Ch3LoanCalculator.java
Step 1: Input Data Values
*/
import java.util.*;
class Ch3LoanCalculator {
public static void main(String[] args) {
double loanAmount,
annualInterestRate;
int loanPeriod;
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
//get input values
System.out.print(Loan Amount (Dollars+Cents): );
loanAmount = scanner.nextDouble();
System.out.print(Annual Interest Rate (e.g., 9.5): );
annualInterestRate = scanner.nextDouble();
System.out.print(Loan Period - # of years: );
loanPeriod = scanner.nextInt();
wu23399_ch03.qxd 12/13/06 17:38 Page 123
3.9 Sample Development—continued
124 Chapter 3 Numerical Data
//echo print the input values
System.out.println ();
System.out.println(Loan Amount: $ + loanAmount);
System.out.println(Annual Interest Rate: 
+ annualInterestRate + %);
System.out.println(Loan Period (years):  + loanPeriod);
}
}
To verify the input routine is working correctly, we run the program multiple times
and enter different sets of data. We make sure the values are displayed in the standard
output window as entered.
Step 2 Development: Output Values
The second step is to add code to display the output values.We will use the standard out-
put window for displaying output values.We need to display the result in a layout that is
meaningful and easy to read. Just displaying numbers such as the following is totally
unacceptable.
132.151.15858.1
We must label the output values so the user can tell what the numbers represent.In addi-
tion, we must display the input values with the computed result so it will not be mean-
ingless. Which of the two shown in Figure 3.6 do you think is more meaningful? The
output format of this program will be
For
Loan Amount: $ amount
Annual Interest Rate: annual interest rate %
Loan Period (years): year
Monthly payment is $ monthly payment
TOTAL payment is $ total payment
with amount,annual interest rate, and others replaced by the actual figures.
step 1 test
step 2
design
wu23399_ch03.qxd 12/13/06 17:38 Page 124
3.9 Sample Development 125
Since the computations for the monthly and total payments are not yet imple-
mented,we will use the following dummy assignment statements:
monthlyPayment = 135.15;
totalPayment = 15858.10;
We will replace these statements with the real ones in the next step.
Here’s our step 2 program with the newly added portion surrounded by a rectangle
and white background:
Only the computed
values (and their
labels) are shown.
Monthly payment: $ 143.47
Total payment: $ 17216.50
Both the input and
computed values (and
their labels) are shown.
For
Loan Amount: $ 10000.00
Annual Interest Rate: 12.0%
Loan Period (years): 10
Monthly payment is $ 143.47
TOTAL payment is $ 17216.50
Figure 3.6 Two different display formats,one with input values displayed and the other with only the
computed values displayed.
step 2 code
/*
Chapter 3 Sample Development: Loan Calculator (Step 2)
File: Step2/Ch3LoanCalculator.java
Step 2: Display the Results
*/
import java.util.*;
class Ch3LoanCalculator {
public static void main(String[] args) {
double loanAmount,
annualInterestRate;
double monthlyPayment,
totalPayment;
int loanPeriod;
wu23399_ch03.qxd 12/13/06 17:38 Page 125
126 Chapter 3 Numerical Data
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
//get input values
System.out.print(Loan Amount (Dollars+Cents): );
loanAmount = scanner.nextDouble();
System.out.print(Annual Interest Rate (e.g., 9.5): );
annualInterestRate = scanner.nextDouble();
System.out.print(Loan Period - # of years: );
loanPeriod = scanner.nextInt();
//compute the monthly and total payments
monthlyPayment = 132.15;
totalPayment = 15858.10;
//display the result
System.out.println();
System.out.println(Loan Amount: $ + loanAmount);
System.out.println(Annual Interest Rate:
+ annualInterestRate + %);
System.out.println(Loan Period (years):  + loanPeriod);
System.out.println(n); //skip two lines
System.out.println(Monthly payment is $  + monthlyPayment);
System.out.println( TOTAL payment is $  + totalPayment);
}
}
To verify the output routine is working correctly,we run the program and verify the
layout.Most likely,we have to run the program several times to fine-tune the arguments for
the println methods until we get the layout that looks clean and nice on the screen.
Step 3 Development: Compute Loan Amount
Wearenowreadytocompletetheprogrambyimplementingtheformuladerivedinthede-
sign phase.The formula requires the monthly interest rate and the number of monthly pay-
ments.The input values to the program,however,are the annual interest rate and the loan
periodinyears.So weneedtoconverttheannualinterestratetoamonthlyinterestrateand
the loan period to the number of monthly payments.The two input values are converted as
monthlyInterestRate = annualInterestRate / 100.0 / MONTHS_IN_YEAR;
numberOfPayments = loanPeriod * MONTHS_IN_YEAR;
step 2 test
step 3
design
3.9 Sample Development—continued
wu23399_ch03.qxd 1/12/07 10:36 Page 126
3.9 Sample Development 127
where MONTHS_IN_YEAR is a symbolic constant with value 12. Notice that we need to
divide the input annual interest rate by 100 first because the formula for loan computa-
tion requires that the interest rate be a fractional value, for example, 0.01, but the input
annual interest rate is entered as a percentage point, for example, 12.0. Please read Exer-
cise 23 on page 142 for information on how the monthly interest rate is derived from a
given annual interest rate.
The formula for computing the monthly and total payments can be expressed as
monthlyPayment = (loanAmount * monthlyInterestRate)
/
(1 - Math.pow( 1 /(1 + monthlyInterestRate),
numberOfPayments) );
totalPayment = monthlyPayment * numberOfPayments;
Let’s put in the necessary code for the computations and complete the program.
Here’s our program:
step 3 code
/*
Chapter 3 Sample Development: Loan Calculator (Step 3)
File: Step3/Ch3LoanCalculator.java
Step 3: Display the Results
*/
import java.util.*;
class Ch3LoanCalculator {
public static void main(String[] args) {
final int MONTHS_IN_YEAR = 12;
double loanAmount,
annualInterestRate;
double monthlyPayment,
totalPayment;
double monthlyInterestRate;
int loanPeriod;
int numberOfPayments;
wu23399_ch03.qxd 12/13/06 17:38 Page 127
3.9 Sample Development—continued
128 Chapter 3 Numerical Data
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
//get input values
System.out.print(Loan Amount (Dollars+Cents): );
loanAmount = scanner.nextDouble();
System.out.print(Annual Interest Rate (e.g., 9.5): );
annualInterestRate = scanner.nextDouble();
System.out.print(Loan Period - # of years: );
loanPeriod = scanner.nextInt();
//compute the monthly and total payments
monthlyInterestRate = annualInterestRate / MONTHS_IN_YEAR / 100;
numberOfPayments = loanPeriod * MONTHS_IN_YEAR;
monthlyPayment = (loanAmount * monthlyInterestRate)/
(1 - Math.pow(1/(1 + monthlyInterestRate),
numberOfPayments ) );
totalPayment = monthlyPayment * numberOfPayments;
//display the result
System.out.println();
System.out.println(Loan Amount: $ + loanAmount);
System.out.println(Annual Interest Rate: 
+ annualInterestRate + %);
System.out.println(Loan Period (years):  + loanPeriod);
System.out.println(n); //skip two lines
System.out.println(Monthly payment is $  + monthlyPayment);
System.out.println( TOTAL payment is $  + totalPayment);
}
}
After the program is coded,we need to run the program through a number of tests.
Since we made the assumption that the input values must be valid, we will test the pro-
gram only for valid input values. If we don’t make that assumption, then we need to test
that the program will respond correctly when invalid values are entered.We will perform
such testing beginning in Chapter 5.To check that this program produces correct results,
step 3 test
wu23399_ch03.qxd 12/13/06 17:38 Page 128
3.9 Sample Development 129
Output
(shown up to three decimal
Input places only)
Annual Loan
Loan Interest Period Monthly Total
Amount Rate (Years) Payment Payment
10000 10 10 132.151 15858.088
15000 7 15 134.824 24268.363
10000 12 10 143.471 17216.514
0 10 5 0.000 0.000
30 8.5 50 0.216 129.373
Step 4 Development: Finishing Up
We finalize the program in the last step by making any necessary modifications or addi-
tions.We will make two additions to the program.The first is necessary while the second
is optional but desirable.The first addition is the inclusion of a program description. One
of the necessary features of any nontrivial program is the description of what the pro-
gram does for the user.We will print out a description at the beginning of the program to
System.out. The second addition is the formatting of the output values. We will format
the monthly and total payments to two decimal places, using a DecimalFormat object.
Here is our final program:
step 4
design
step 4 code
/*
Chapter 3 Sample Development: Loan Calculator (Step 4)
File: Step4/Ch3LoanCalculator.java
Step 4: Finalize the program
*/
import java.util.*;
import java.text.*;
class Ch3LoanCalculator {
public static void main(String[] args) {
final int MONTHS_IN_YEAR = 12;
we can run the program with the following input values.The right two columns show the
correct results.Try other input values as well.
wu23399_ch03.qxd 12/13/06 17:38 Page 129
3.9 Sample Development—continued
130 Chapter 3 Numerical Data
double loanAmount,
annualInterestRate;
double monthlyPayment,
totalPayment;
double monthlyInterestRate;
int loanPeriod;
int numberOfPayments;
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
DecimalFormat df = new DecimalFormat(0.00);
//describe the program
System.out.println(This program computes the monthly and total);
System.out.println(payments for a given loan amount, annual );
System.out.println(interest rate, and loan period.);
System.out.println(Loan amount in dollars and cents,
e.g., 12345.50);
System.out.println(Annual interest rate in percentage,
e.g., 12.75);
System.out.println(Loan period in number of years, e.g., 15);
System.out.println(n); //skip two lines
//get input values
System.out.print(Loan Amount (Dollars+Cents): );
loanAmount = scanner.nextDouble( );
System.out.print(Annual Interest Rate (e.g., 9.5): );
annualInterestRate = scanner.nextDouble( );
System.out.print(Loan Period - # of years: );
loanPeriod = scanner.nextInt( );
//compute the monthly and total payments
monthlyInterestRate = annualInterestRate / MONTHS_IN_YEAR / 100;
numberOfPayments = loanPeriod * MONTHS_IN_YEAR;
monthlyPayment = (loanAmount * monthlyInterestRate) /
(1 - Math.pow(1/(1 + monthlyInterestRate),
numberOfPayments ) );
totalPayment = monthlyPayment * numberOfPayments;
wu23399_ch03.qxd 12/13/06 17:38 Page 130
3.10 Numerical Representation (Optional)
In this section we explain how integers and real numbers are stored in memory.
Although computer manufacturers have used various formats for storing numerical
values, today’s standard is to use the twos complement format for storing integers
and the floating-point format for real numbers. We describe these formats in this
section.
An integer can occupy 1, 2, 4, or 8 bytes depending on which data type
(i.e., byte, short, int, or long) is declared. To make the examples easy to follow,
we will use 1 byte ( 8 bits) to explain twos complement form. The same principle
applies to 2, 4, and 8 bytes. (They just utilize more bits.)
3.10 Numerical Representation (Optional) 131
//display the result
System.out.println();
System.out.println(Loan Amount: $ + loanAmount);
System.out.println(Annual Interest Rate: 
+ annualInterestRate + %);
System.out.println(Loan Period (years):  + loanPeriod);
System.out.println(n); //skip two lines
System.out.println(Monthly payment is $ 
+ df.format(monthlyPayment));
System.out.println( TOTAL payment is $ 
+ df.format(totalPayment));
}
}
We repeat the test runs from step 3 and confirm the modified program still runs
correctly. Since we have not made any substantial additions or modifications, we fully
expect the program to work correctly. However, it is very easy to introduce errors in cod-
ing,so even if we think the changes are trivial,we should never skip the testing after even
a slight modification.
step 4 test
Always test after making any additions or modifications to a program,no matter
how trivial you think the changes are.
twos
complement
wu23399_ch03.qxd 12/13/06 17:38 Page 131
132 Chapter 3 Numerical Data
The following table shows the first five and the last four of the 256 positive
binary numbers using 8 bits. The right column lists their decimal equivalents.
8-Bit Binary Number Decimal Equivalent
00000000 0
00000001 1
00000010 2
00000011 3
00000100 4
. . .
11111100 252
11111101 253
11111110 254
11111111 255
Using 8 bits, we can represent positive integers from 0 to 255. Now let’s see
the possible range of negative and positive numbers that we can represent, using
8 bits. We can designate the leftmost bit as a sign bit: 0 means positive and 1 means
negative. Using this scheme, we can represent integers from 127 to 127 as
shown in the following table:
sign bit
8-Bit Binary Number
(with a Sign Bit) Decimal Equivalent
0 0000000 0
0 0000001 1
0 0000010 2
. . .
0 1111111 127
1 0000000 0
1 0000001 1
. . .
1 1111110 126
1 1111111 127
Notice that zero has two distinct representations (0  00000000 and 0 
10000000), which adds complexity in hardware design. Twos complement format
avoids this problem of duplicate representations for zero. In twos complement for-
mat, all positive numbers have zero in their leftmost bit. The representation of a
negative number is derived by first inverting all the bits (changing 1s to 0s and 0s to
wu23399_ch03.qxd 12/13/06 17:38 Page 132
3.10 Numerical Representation (Optional) 133
1s) in the representation of the positive number and then adding 1. The following
diagram illustrates the process:
13 = 00001101
invert
11110010
add 1
-13 = 11110011
The following table shows the decimal equivalents of 8-bit binary numbers by
using twos complement representation. Notice that zero has only one representation.
8-Bit Binary Number Decimal
(Twos Complement) Equivalent
00000000 0
00000001 1
00000010 2
. . .
01111111 127
10000000 128
10000001 127
. . .
11111110 2
11111111 1
Now let’s see how real numbers are stored in memory in floating-point format.
We present only the basic ideas of storing real numbers in computer memory here.
We omit the precise details of the Institute of Electronics and Electrical Engineers
(IEEE) Standard 754 that Java uses to store real numbers.
Real numbers are represented in the computer by using scientific notation. In
base-10 scientific notation, a real number is expressed as
A  10N
where A is a real number and N is an integral exponent. For example, the mass of a
hydrogen atom (in grams) is expressed in decimal scientific notation as 1.67339 
10–24
, which is equal to 0.00000000000000000000000167339.
We use base-2 scientific notation to store real numbers in computer memory.
Base-2 scientific notation represents a real number as follows:
A  2N
The float and double data types use 32 and 64 bits, respectively, with the num-
ber A and exponent N stored as follows:
floating-point
wu23399_ch03.qxd 12/13/06 17:38 Page 133
134 Chapter 3 Numerical Data
8
1
A
N
S
1
A
N
S
11 52
23
Sign bit:
0 — positive
1 — negative
Number of
bits used
The value A is a normalized fraction, where the fraction begins with a binary point,
followed by a 1 bit and the rest of the fraction. ( Note: A decimal number has a dec-
imal point; a binary number has a binary point.) The following numbers are sample
normalized and unnormalized binary fractions:
normalized
fraction
Normalized Unnormalized
1.1010100 1.100111
1.100011 .0000000001
1.101110011 .0001010110
Since a normalized number always start with a 1, this bit does not actually
have to be stored. The following diagram illustrates how the A value is stored.
The sign bit S indicates the sign of a number, so A is stored in memory as an un-
signed number. The integral exponent N can be negative or positive. Instead of using
twos complement for storing N, we use a format called excess format. The 8-bit ex-
ponent uses the excess-127 format, and the 11-bit exponent uses the excess-1023
format. We will explain the excess-127 format here. The excess-1023 works
similarly. With the excess-127 format, the actual exponent is computed as
N  127
Therefore, the number 127 represents an exponent of zero. Numbers less than 127
represent negative exponents, and numbers greater than 127 represent positive
exponents. The following diagram illustrates that the number 125 in the exponent
field represents 2125127
 22
.
N
01111101
A
S
201111101127
 2125127
 22
8
1
0 1 1 0 1 1 1 0 0 ... 0 0
0 1 1 0 1 1 1
.1
N
S
excess format
wu23399_ch03.qxd 12/13/06 17:38 Page 134
Key Concepts 135
• A variable is a memory location in which to store a value.
• A variable has a name and a data type.
• A variable must be declared before we can assign a value to it.
• There are six numerical data types in Java: byte, short, int, long, float, and
double.
• Object names are synonymous with variables whose contents are memory
addresses.
• Numerical data types are called primitive data types, and objects are called
reference data types.
• Precedence rules determine the order of evaluating arithemetic expressions.
• Symbolic constants hold values just as variables do, but we cannot change
their values.
• The standard classes introduced in this chapter are
Math
GregorianCalendar
DecimalFormat
PrintStream
• System.out is used to output multiple lines of text to the standard output window.
• System.in is used to input a stream of bytes. We associate a Scanner object to
System.in to input primitive data type.
• The Math class contains many class methods for mathematical functions.
• The GregorianCalendar class is used in the manipulation of calendar
information.
• The DecimalFormat class is used to format numerical data.
• (Optional) Twos complement format is used for storing integers, and
floating-pointing format is used for storing real numbers.
S u m m a r y
K e y C o n c e p t s
variables
primitive data types
reference data types
arithmetic expression
arithmetic operators
precedence rules
typecasting
implicit and explicit casting
assignment conversion
constants
standard output
standard input
echo printing
twos complement (optional)
floating point (optional)
wu23399_ch03.qxd 12/13/06 17:38 Page 135
136 Chapter 3 Numerical Data
E x e r c i s e s
1. Suppose we have the following declarations:
int i = 3, j = 4, k = 5;
float x = 34.5f, y = 12.25f;
Determine the value for each of the following expressions, or explain why it
is not a valid expression.
a. (x + 1.5) / (250.0 * (i/j))
b. x + 1.5 / 250.0 * i / j
c. -x * -y * (i + j) / k
d. (i / 5) * y
e. Math.min(i, Math.min(j,k))
f. Math.exp(3, 2)
g. y % x
h. Math.pow(3, 2)
i. (int)y % k
j. i / 5 * y
2. Suppose we have the following declarations:
int m, n, i = 3, j = 4, k = 5;
float v, w, x = 34.5f, y = 12.25f;
Determine the value assigned to the variable in each of the following
assignment statements, or explain why it is not a valid assignment.
a. w = Math.pow(3,Math.pow(i,j));
b. v = x / i;
c. w = Math.ceil(y) % k;
d. n = (int) x / y * i / 2;
e. x = Math.sqrt(i*i - 4*j*k);
f. m = n + i * j;
g. n = k /(j * i) * x + y;
h. i = i + 1;
i. w = float(x + i);
j. x = x / i / y / j;
3. Suppose we have the following declarations:
int i, j;
float x, y;
double u, v;
Which of the following assignments are valid?
a. i = x;
b. x = u + y;
c. x = 23.4 + j * y;
d. v = (int) x;
e. y = j / i * x;
wu23399_ch03.qxd 12/13/06 17:38 Page 136
4. Write Java expressions to compute each of the following.
a. The square root of B2
 4AC (A and C are distinct variables)
b. The square root of X  4Y3
c. The cube root of the product of X and Y
d. The area R2
of a circle
5. Determine the output of the following program without running it.
class TestOutputBox {
public static void main(String[] args) {
System.out.println(One);
System.out.print(Two);
System.out.print(n);
System.out.print(Three);
System.out.println(Four);
System.out.print(n);
System.out.print(Five);
System.out.println(Six);
}
}
6. Determine the output of the following code.
int x, y;
x = 1;
y = 2;
System.out.println(The output is  + x + y );
System.out.println(The output is  + ( x + y) );
7. Write an application that displays the following pattern in the standard
output window.
Note: The output window is not drawn to scale.
8. Write an application to convert centimeters (input) to feet and inches
(output). 1 in  2.54 cm.
OXOXOXOXOXOXOXOXOXOX
X O
O X
X O
O X
X O
OXOXOXOXOXOXOXOXOXOX
Starts from the second
line with five leading
blank spaces.
Exercises 137
wu23399_ch03.qxd 12/13/06 17:38 Page 137
9. Write an application that inputs temperature in degrees Celsius and prints
out the temperature in degrees Fahrenheit. The formula to convert degrees
Celsius to equivalent degrees Fahrenheit is
Fahrenheit  1.8  Celsius  32
10. Write an application that accepts a person’s weight and displays the number
of calories the person needs in one day. A person needs 19 calories per
pound of body weight, so the formula expressed in Java is
calories = bodyWeight * 19;
(Note: We are not distinguishing between genders.)
11. A quantity known as the body mass index (BMI) is used to calculate the risk
of weight-related health problems. BMI is computed by the formula
BMI  
(h10
w
0.0)2

where w is weight in kilograms and h is height in centimeters. A BMI of
about 20 to 25 is considered “normal.” Write an application that accepts
weight and height (both integers) and outputs the BMI.
12. Your weight is actually the amount of gravitational attraction exerted on you
by the Earth. Since the Moon’s gravity is only one-sixth of the Earth’s gravity,
on the Moon you would weigh only one-sixth of what you weigh on Earth.
Write an application that inputs the user’s Earth weight and outputs her or his
weight on Mercury, Venus, Jupiter, and Saturn. Use the values in this table.
Planet Multiply the Earth Weight by
Mercury 0.4
Venus 0.9
Jupiter 2.5
Saturn 1.1
13. When you say you are 18 years old, you are really saying that the Earth has
circled the Sun 18 times. Since other planets take fewer or more days than
Earth to travel around the Sun, your age would be different on other planets.
You can compute how old you are on other planets by the formula
y  
x 
d
365

where x is the age on Earth, y is the age on planet Y, and d is the number of
Earth days the planet Y takes to travel around the Sun. Write an application
that inputs the user’s Earth age and print outs his or her age on Mercury,
Venus, Jupiter, and Saturn. The values for d are listed in the table.
138 Chapter 3 Numerical Data
wu23399_ch03.qxd 12/13/06 17:38 Page 138
d  Approximate Number of Earth
Days for This Planet to Travel
Planet around the Sun
Mercury 88
Venus 225
Jupiter 4,380
Saturn 10,767
14. Write an application to solve quadratic equations of the form
Ax2
 Bx  C  0
where the coefficients A, B, and C are real numbers. The two real number
solutions are derived by the formula
x  
B 
2
B
A
2
 4
AC


For this exercise, you may assume that A 0 and the relationship
B2
4AC
holds, so there will be real number solutions for x.
15. Write an application that determines the number of days in a given
semester. Input to the program is the year, month, and day information of
the first and the last days of a semester. Hint: Create GregorianCalendar
objects for the start and end dates of a semester and manipulate their
DAY_OF_YEAR data.
16. Modify the Ch3FindDayOfWeek program by accepting the date information
as a single string instead of accepting the year, month, and day information
separately. The input string must be in the MM/dd/yyyy format. For
example, July 4, 1776, is entered as 07/04/1776. There will be exactly two
digits for the month and day and four digits for the year.
17. Write an application that accepts the unit weight of a bag of coffee in pounds
and the number of bags sold and displays the total price of the sale, computed as
totalPrice = unitWeight * numberOfUnits * 5.99;
totalPriceWithTax = totalPrice + totalPrice * 0.0725;
where 5.99 is the cost per pound and 0.0725 is the sales tax. Display the
result in the following manner:
Draw the program diagram.
Number of bags sold: 32
Weight per bag: 5 lb
Price per pound: $5.99
Sales tax: 7.25%
Total price: $ 1027.884
Exercises 139
wu23399_ch03.qxd 12/13/06 17:38 Page 139
18. If you invest P dollars at R percent interest rate compounded annually, in
N years, your investment will grow to
P(1  R100)N
dollars. Write an application that accepts P, R, and N and computes the
amount of money earned after N years.
19. Leonardo Fibonacci of Pisa was one of the greatest mathematicians of the
Middle Ages. He is perhaps most famous for the Fibonacci sequence, which
can be applied to many diverse problems. One amusing application of the
Fibonacci sequence is in finding the growth rate of rabbits. Suppose a pair of
rabbits matures in 2 months and is capable of reproducing another pair every
month after maturity. If every new pair has the same capability, how many
pairs will there be after 1 year? (We assume here that no pairs die.) The table
below shows the sequence for the first 7 months. Notice that at the end of the
second month, the first pair matures and bears its first offspring in the third
month, making the total two pairs.
Month No. Number of Pairs
1 1
2 1
3 2
4 3
5 5
6 8
7 13
The Nth Fibonacci number in the sequence can be evaluated with the
formula
FN  

1
5



1 
2
5



N


1 
2
5



N

Write an application that accepts N and displays FN. Note that the result of
computation using the Math class is double. You need to display it as an
integer.
20. According to Newton’s universal law of gravitation, the force F between two
bodies with masses M1 and M2 is computed as
F  k 
M
d
1M
2
2

where d is the distance between the two bodies and k is a positive real
number called the gravitational constant. The gravitational constant k is
approximately equal to 6.67E-8 dyn  cm2
/g2
. Write an application that
140 Chapter 3 Numerical Data
wu23399_ch03.qxd 12/13/06 17:39 Page 140
(1) accepts the mass for two bodies in grams and the distance between the
two bodies in centimeters and (2) computes the force F. Use the standard
input and output, and format the output appropriately. For your information,
the force between the Earth and the Moon is 1.984E25 dyn. The mass of the
earth is 5.983E27 g, the mass of the moon is 7.347E25 g, and the distance
between the two is 3.844E10 cm.
21. Dr. Caffeine’s Law of Program Readability states that the degree of program
readability R (whose unit is mocha) is determined as
R  k 
C
V
T
3
2

where k is Ms. Latte’s constant, C is the number of lines in the program that
contain comments, T is the time spent (in minutes) by the programmer
developing the program, and V is the number of lines in the program that
contain nondescriptive variable names. Write an application to compute the
program readability R. Ms. Latte’s constant is 2.5E2 mocha lines2
/min2
.
(Note: This is just for fun. Develop your own law, using various functions
from the Math class.)
22. If the population of a country grows according to the formula
y  cekx
where y is the population after x years from the reference year, then we can
determine the population of a country for a given year from two census
figures. For example, given that a country with a population of 1,000,000 in
1970 grows to 2,000,000 by 1990, we can predict the country’s population in
the year 2000. Here’s how we do the computation. Letting x be the number
of years after 1970, we obtain the constant c as 1,000,000 because
1,000,000  cek0
 c
Then we determine the value of k as
y  1,000,000ekx

2
1
,
,
0
0
0
0
0
0
,
,
0
0
0
0
0
0
  e20k
k  
2
1
0
 ln 
2
1
,
,
0
0
0
0
0
0
,
,
0
0
0
0
0
0
  0.03466
Finally we can predict the population in the year 2000 by substituting
0.03466 for k and 30 for x (2000  1970  30). Thus, we predict
y  1,000,000e0.03466(30)
 2,828,651
as the population of the country for the year 2000. Write an application that
accepts five input values—year A, population in year A, year B, population
in year B, and year C—and predict the population for year C.
Exercises 141
wu23399_ch03.qxd 12/13/06 17:39 Page 141
23. In Section 3.9, we use the formula
MR  
A
1
R
2

to derive the monthly interest rate from a given annual interest rate,
where MR is the monthly interest rate and AR is the annual interest
rate (expressed in a fractional value such as 0.083). This annual interest
rate AR is called the stated annual interest rate to distinguish it from the
effective annual interest rate, which is the true cost of a loan. If the
stated annual interest rate is 9 percent, for example, then the effective
annual interest rate is actually 9.38 percent. Naturally, the rate that the
financial institutions advertise more prominently is the stated interest
rate. The loan calculator program in Section 3.9 treats the annual
interest rate that the user enters as the stated annual interest rate. If the
input is the effective annual interest rate, then we compute the monthly
rate as
MR  (1  EAR)112
1
where EAR is the effective annual interest rate. The difference between
the stated and effective annual interest rates is negligible only when
the loan amount is small or the loan period is short. Modify the loan
calculator program so that the interest rate that the user enters is
treated as the effective annual interest rate. Run the original and modified
loan calculator programs, and compare the differences in the monthly
and total payments. Use loan amounts of 1, 10, and 50 million dollars
with loan periods of 10, 20, and 30 years and annual interest rates of
0.07, 0.10, and 0.18 percent, respectively. Try other combinations also.
Visit several websites that provide a loan calculator for computing
a monthly mortgage payment (one such site is the financial page at
www.cnn.com). Compare your results to the values computed by the
websites you visited. Determine whether the websites treat the input
annual interest rate as stated or effective.
Development Exercises
For the following exercises, use the incremental development methodology
to implement the program. For each exercise, identify the program tasks,
create a design document with class descriptions, and draw the program
diagram. Map out the development steps at the start. State any assumptions
you must make about the input. Present any design alternatives and justify
your selection. Be sure to perform adequate testing at the end of each
development step.
24. Develop an application that reads a purchase price and an amount tendered
and then displays the change in dollars, quarters, dimes, nickels, and
142 Chapter 3 Numerical Data
wu23399_ch03.qxd 12/13/06 17:39 Page 142
pennies. Two input values are entered in cents, for example, 3480 for $34.80
and 70 for $0.70. Display the output in the following format:
Notice the input values are to be entered in cents (int data type), but
the echo printed values must be displayed with decimal points (float
data type).
25. MyJava Coffee Outlet runs a catalog business. It sells only one type of
coffee beans, harvested exclusively in the remote area of Irian Jaya. The
company sells the coffee in 2-lb bags only, and the price of a single 2-lb
bag is $5.50. When a customer places an order, the company ships the
order in boxes. The boxes come in three sizes: the large box holds 20 bags
of 2 lb, the medium 10 bags, and the small 5 bags. The cost of a large
box is $1.80; a medium box, $1.00; and a small box, $0.60. The order is
shipped using the least number of boxes. For example, the order of
52 bags will be shipped in two boxes, one large and one small. Develop
an application that computes the total cost of an order. Display the output
in the following format:
Number of Bags Ordered: 52 - $ 286.00
Boxes Used:
2 Large - $3.60
1 Medium - $1.00
1 Small - $0.60
Your total cost is: $ 291.20
26. Repeat Exercise 25, but this time, accept the date when the order is placed
and display the expected date of arrival. The expected date of arrival is two
weeks (14 days) from the date of order. The order date is entered as a
single string in the MM/dd/yyyy format. For example, November 1, 2004
is entered as 11/01/2004. There will be exactly two digits each for the
Purchase Price: $ 34.80
Amount Tendered: $ 40.00
Your change is: $ 5.20
5 one-dollar bill(s)
0 quarter(s)
2 dime(s)
0 nickel(s)
0 penn(y/ies)
Thank you for your business. Come back soon.
Exercises 143
wu23399_ch03.qxd 12/13/06 17:39 Page 143
month and day and four digits for the year. Display the output in the
following format:
Number of Bags Ordered: 52 - $ 286.00
Boxes Used:
2 Large - $3.60
1 Medium - $1.00
1 Small - $0.60
Your total cost is: $ 291.20
Date of Order: November 1, 2004
Expected Date of Arrival: November 15, 2004
27. Using a Turtle object from the galapagos package, draw three rectangles.
Accept the width and the length of the smallest rectangle from the user. The
middle and the largest rectangles are 40 and 80 percent larger, respectively,
than the smallest rectangle. The galapagos package and its documentation
are available at www.drcaffeine.com.
28. Develop a program that draws a bar chart using a Turtle object. Input five int
values, and draw the vertical bars that represent the entered values in the
following manner:
Your Turtle must draw everything shown in the diagram, including the axes
and numbers.
10
5
7
12
3
144 Chapter 3 Numerical Data
wu23399_ch03.qxd 12/13/06 17:39 Page 144
Defining Your Own
Classes—Part 1
O b j e c t i v e s
After you have read and studied this chapter,you should be able to
• Define a class with multiple methods and data
members.
• Differentiate the local and instance variables.
• Define and use value-returning methods.
• Distinguish private and public methods.
• Distinguish private and public data members.
• Pass both primitive data and objects to a
method.
145
4
wu23399_ch04.qxd 12/13/06 17:59 Page 145
146 Chapter 4 Defining Your Own Classes—Part 1
I n t r o d u c t i o n
o far we have been using only standard classes such as System, String, and others
when we wrote programs. For a basic program, that is fine. However, we need to
to learn how to write programs using our own classes (in addition to using the stan-
dard classes) when the programs become large and complex. In this chapter, we
learn the basics of how to define our own classes. And, in Chapter 7, we will cover
more advanced topics on defining classes.
4.1 First Example: Defining and Using a Class
The most economical and effective means of on-campus transportation is without
doubt a bicycle. Suppose we want to develop a program that tracks the bicycles by
assigning to them some form of identification number along with the relevant
information, such the owner’s name and phone number. To develop such a Java
program, we need to design many different types of objects. For example, we need
objects to handle input, output, data storage, and other computational tasks. Among
the many types of objects necessary for this program, we will design a core class
that models a bicycle. There’s no such Bicycle class among the standard classes, of
course, so we need to define one ourselves. We will learn how to define the Bicycle
class in this section. We will start with a very simplistic Bicycle class. Using this
class, we can only assign and retrieve the owner’s name. Before we look inside the
Bicycle class and explain how the class is defined, let’s first look at how we might
use it in our program. The following sample program creates two Bicycle objects,
assigns the owners’ names to them, and displays the information:
class BicycleRegistration {
public static void main(String[] args) {
Bicycle bike1, bike2;
String owner1, owner2;
bike1 = new Bicycle( ); //Create and assign values to bike1
bike1.setOwnerName(Adam Smith);
bike2 = new Bicycle( ); //Create and assign values to bike2
bike2.setOwnerName(Ben Jones);
//Output the information
owner1 = bike1.getOwnerName( );
owner2 = bike2.getOwnerName( );
System.out.println(owner1 +  owns a bicycle.);
System.out.println(owner2 +  also owns a bicycle.);
}
}
S
wu23399_ch04.qxd 12/13/06 17:59 Page 146
The dependency diagram between the two classes is as follows:
When this program is executed, we get the following output on the standard output
window:
Adam Smith owns a bicycle.
Ben Jones also owns a bicycle.
This main class should look very familiar to all of us. The key difference lies in
the use of the Bicycle class instead of the standard classes we have been using so far.
The way we use the Bicycle class is the same. For example, we create a Bicycle object
bike2 by calling the new operator, and we assign the name of its owner by executing
bike2 = new Bicycle( );
bike2.setOwnerName(Ben Jones);
BicycleRegistration Bicycle
Here’s the definition of the Bicycle class. To distinguish it from the standard
classes, we call the Bicycle and other classes we define programmer-defined classes.
4.1 First Example:Defining and Using a Class 147
programmer-
defined classes
class Bicycle {
// Data Member
private String ownerName;
//Constructor: Initialzes the data member
public Bicycle( ) {
ownerName = Unknown;
}
//Returns the name of this bicycle's owner
public String getOwnerName( ) {
return ownerName;
}
//Assigns the name of this bicycle's owner
public void setOwnerName(String name) {
ownerName = name:
}
}
wu23399_ch04.qxd 12/13/06 17:59 Page 147
To get the name of the owner of bike2, we write
bike2.getOwnerName()
And we can assign the returned value to a variable if we write
String owner2;
...
owner2 = bike2.getOwnerName();
Although it is not a requirement, we will save one class definition per file to keep
things simple. For the file name, we will use the name of the class followed by the
java suffix. So, we save the Bicycle class in a file named Bicycle.java.
148 Chapter 4 Defining Your Own Classes—Part 1
Save one class definition per file.Use the name of the class followed by the suffix
java as the file name.Follow this rule to avoid any unnecessary complications.
For this sample program, we have created two classes—BicycleRegistration (the
main class) and Bicycle. So there are two source files for this program.
The Bicycle Class
Now let’s study the Bicycle class. Table 4.1 lists the three methods of the Bicycle
class and their description.
Here’s a template for the Bicycle class declaration:
class Bicycle {
//data members
//methods
}
BicycleRegistration.java Bicycle.java
Table
Table 4.1 The three methods of the Bicycle class.The first method is called
a constructor
Method Parameter Description
Bicycle None Initializes the owner’s name to Unassigned.
getOwnerName None Returns the owner’s name.
setOwnerName Name of the Assigns the bicycle owner’s name to the passed
owner (string) value.
wu23399_ch04.qxd 12/13/06 18:00 Page 148
The class declaration begins with the reserved word class followed by the name.
Any valid identifier that is not a reserved word can be used as the class name.
We define the three methods inside the class declaration. But before we can
provide the method definitions, we need to consider the data members of the Bicycle
class. Remember, in Section 1.3, we stated that data members of a class are the data
values associated with the class or instances of the class, such as the current balance
of an Account object. What would be the data members of Bicycle objects? We need
to know the owner’s name of every Bicycle object, so we’ll define one data member
to store the owner’s name. The data members of a class are declared within the class
declaration. Here’s how we define the data member ownerName of the Bicycle class:
class Bicycle {
private String ownerName;
//definitions for the constructor,
//getOwnerName, and setOwnerName methods come here
}
The ownerName data member is an instance variable (we will learn how to de-
clare class constants later in this chapter and class variables in Chap. 7). Remember
that, in Section 1.3, we defined an instance variable as the data member we associate
to an individual instance and whose value can change over time. In other words, each
instance of the class will have its own copy. After the two Bicycle objects are created
and assigned their respective names by program, we have the following memory state:
The syntax for the data member declaration is
modifier-list data type name ;
where modifier-list designates different characteristics of the data member, data
type the class name or primitive data type, and name the name of the data mem-
ber. Here’s how the general syntax corresponds to the actual declaration:
Modifier
private ownerName ;
Data Type
String
Name
bike1
“Adam Smith”
:Bicycle
ownerName
bike2
“Ben Jones”
:Bicycle
ownerName
4.1 First Example:Defining and Using a Class 149
wu23399_ch04.qxd 12/13/06 18:00 Page 149
In this example, the data member has one modifier named private. This modifier is
called an accessibility modifier, or a visibility modifier, and it restricts who can have
a direct access to the data member. If the modifier is private, then only the methods
defined in the class can access it directly. We will provide a more detailed discus-
sion of the accessibility modifiers in Section 4.6. For now, it suffices to remember
that data members are declared private for the most part.
Now that the necessary data member is taken care of, we are ready to define the
three methods. We start with the setOwnerName method, which is declared as
public void setOwnerName(String name) {
ownerName = name;
}
The syntax for defining a method, as given in Chapter 2, is
modifiers return type method name ( parameters ) {
statements
}
The following diagram shows how the components in the general syntax cor-
respond to the actual elements in the setOwnerName method:
We explained in Chapter 1 that methods may or may not return a value. A
method that does not return a value, such as this setOwnerName method, is declared
as void. It is called a void method. The accessibility modifier for the setOwnerName
method is declared as public. This means the program that uses the Bicycle class can
access, or call, this method. It is possible (and could be useful) to declare a method
as private. If a method is declared as private, then it cannot be called from the pro-
gram that uses the class. It can only be called from the other methods of the same
class. For now we will limit our discussion to public methods. Here we declare all
methods as public because we want the programs that use the Bicycle class to be able
to call them. We will go over the use of private methods later in the chapter.
Modifier
public
}
setOwnerName ( String name ) {
Statements
Return Type
void
Method Name
Parameter
ownerName = name;
This refers to
instance variable
ownerName.
This refers to
parameter name.
150 Chapter 4 Defining Your Own Classes—Part 1
accessibility
modifier
void method
wu23399_ch04.qxd 12/13/06 18:00 Page 150
The getOwnerName method is defined as follows:
public String getOwnerName( ) {
return ownerName;
}
The following diagram shows how the components in the general syntax corre-
spond to the actual elements in the getOwnerName method:
This is a value-returning method. When this method is called, it returns a
value to the caller. The getOwnerName method returns a string value—the value of
instance variable ownerName—so its return type is declared as String. A value-
returning method must include a return statement of the format
return expression ;
The data type of expression must be compatible with the declared return
type of the method. For example, if the return type is int, then the data type of the
returned value must be compatible with int (data types int, short, and byte are all
compatible with int). Data type compatibilites are explained in Section 3.2.
If a method returns a value, then we can include a call to the method in an ex-
pression itself. For example, instead of writing
Bicycle bike;
...
String owner = bike.getOwnerName( );
System.out.println(owner + owns a bike.);
we can write
Bicycle bike;
...
System.out.println(bike.getOwnerName( ) + owns a bike.);
Modifier
public
}
getOwnerName ( ) {
Statements
Return Type
String
Method Name
Parameter
return ownerName;
This refers to
instance variable
ownerName.
4.1 First Example:Defining and Using a Class 151
value-returning
method
return state-
ment syntax
wu23399_ch04.qxd 12/13/06 18:00 Page 151
A method that returns information about an object (such as who is the owner
of a bicycle) is called an accessor. The getOwnerName method is an accessor. An
inverse of an accessor that sets a property of an object is called a mutator. The
setOwnerName method is a mutator. Accessors and mutators are commonly called
get and set methods, respectively.
A value-returning method can include more than one return statement. The
use of multiple return statements make sense only in the context of the control
statements, which we will discuss in Chapters 5 and 6. We will be seeing examples
of multiple return statements in these chapters.
The first method defined in the Bicycle class is a special method called a con-
structor. A constructor is a special method that is executed when a new instance of
the class is created, that is, when the new operator is called. Here’s the constructor
for the Bicycle class:
public Bicycle( ) {
ownerName = Unassigned;
}
It follows the general syntax
public class name ( parameters ) {
statements
}
where class name is the name of the class to which this constructor belongs. The
following diagram shows how the components in the general syntax correspond to
the actual elements in the constructor of the Bicycle class:
Notice that a constructor does not have a return type and, consequently, will
never include a return statement. The modifier of a constructor does not have to be
public, but non-public constructors are rarely used. This example shows no parame-
ters, but it is very common to define a constructor with two or three parameters. We
will see an example of a constructor that accepts two parameters in Section 4.5.
Until then, we will define only a zero-parameter constructor.
The purpose of the Bicycle constructor is to initialize the data member to a
value that reflects the state to which the real name is not yet assigned. Since a
constructor is executed when a new instance is created, it is the most logical place
Modifier
public
}
( ) {
Statements
Class Name
Bicycle
Parameters
ownerName = Unassigned;
152 Chapter 4 Defining Your Own Classes—Part 1
accessor
mutator
constructor
wu23399_ch04.qxd 12/13/06 18:00 Page 152
to initialize the data members and perform any other initialization tasks. Figure 4.1
shows a sequence of state-of-memory diagrams illustrating the effects of executing
the constructor and the setOwnerName method of the Bicycle class.
We stated earlier that the Bicycle class has three methods, of which one is a
constructor. However, a constructor is distinct from other “regular” methods, so it is
more common to state that the Bicycle class has one constructor and two methods.
4.1 First Example:Defining and Using a Class 153
Figure 4.1 A sequence of state-of-memory diagrams that illustrate the effects of executing the constructor
and the setOwnerName method of the Bicycle class.
bike
bike
“Unassigned”
:Bicycle
ownerName
Bicycle bike;
bike = new Bicycle( );
Bicycle bike;
bike
“Jon Java”
:Bicycle
ownerName
Bicycle bike;
bike = new Bicycle( );
bike.setOwnerName(Jon Java);
Instead of saying“a class has three methods including one constructor,”it is more
common to say“a class has one constructor and two methods.”We will use the later
expression in this book.
wu23399_ch04.qxd 12/13/06 18:00 Page 153
We will provide a more detailed discussion on constructors in Section 4.5.
The class diagram that lists the data member, the constructor, and two meth-
ods of the Bicycle class is shown in Figure 4.2.
In listing the data members and methods of a class, we will use the following
convention:
We list the data members first, then the constructor, and finally the methods.
Within each group, we list elements in alphabetical order. Keep in mind that
this convention for grouping elements and ordering them within a group is for our
convenience. The Java compiler does not care how we list the data members and
methods.
class class name {
// data members
// constructor
// methods
}
154 Chapter 4 Defining Your Own Classes—Part 1
Data Member Listing
Method Listing
We include the data type
of an argument passed to
the method.
Bicycle
ownerName
Bicycle( )
getOwnerName( )
setOwnerName( String )
Figure 4.2 A class diagram of the Bicycle class with two methods and one data member.
class listing
convention
The Java compiler does not care how we order the methods and data members in
the class declaration.We adopt the listing convention to make the class declaration
easier for us to follow.
Compiling and Running BicycleRegistration
Up until now, when we ran the sample programs, we simply compiled and executed
the main class. That’s all we need to do because the main class is the only class we
wu23399_ch04.qxd 12/13/06 18:00 Page 154
created for the sample programs. But for this sample program, we have created two
classes—BicycleRegistration (the main class) and Bicycle. So there are two source
files for this program.
From now on, we will use the name of the main class to refer the whole pro-
gram. To run the BicycleRegistration program, we must first compile the two source
files and then run the main class. Here are the steps we follow to run this sample
program (we will illustrate the steps using the minimalist approach, see App. A):
1. Compile the Bicycle class.
javac Bicycle.java
2. Compile the BicycleRegistration class.
javac BicycleRegistration.java
3. Run the BicycleRegistration class.
java BicycleRegistration
There is one last thing to remember. The way the classes are written now, the
easiest way to manage a program that includes multiple programmer-defined
classes is to save the source files in the same folder (directory). We will learn how
to organize classes into a package in Chapter 7 so we can manage the organization
of classes in a more effective manner. Until then, just remember to place all sources
files for a program in the same folder. If you don’t do this, the Java compiler and
interpreter may not be able to compile and run the program.
BicycleRegistration.java
Source files for the BicycleRegistration program
Bicycle.java
These files must
be stored in the
same folder.
4.1 First Example:Defining and Using a Class 155
Place all source files for a program in the same folder (directory).
It is not necessary to create a separate folder for each program, though. In
other words, one folder can contain source files for multiple programs. For example,
wu23399_ch04.qxd 12/13/06 18:00 Page 155
we could create one folder to place all source files for this chapter’s sample code.
However, we recommend that students create a separate folder for each program-
ming assignment or lab project for easy management.
156 Chapter 4 Defining Your Own Classes—Part 1
The class declaration can be preceded with the accessibility modifier public or
private. For now, we do not use any accessibility modifier for the class dec-
laration. We will discuss the issue when we discuss a package organization in
Chapter 7.
1. Extend the Bicycle class by adding the second data member tagNo of type
String. Declare this data member as private.
2. Add a new method to the Bicycle class that assigns a tag number. This method
will be called as follows:
Bicycle bike;
bike = new Bicycle( );
...
bike.setTagNo(2004–134R);
3. Add a another method to the Bicycle class that returns the bicycle’s tag number.
This method will be called as follows:
Bicycle bike;
bike = new Bicycle( );
...
String tag = bike.getTagNo( );
4.2 Second Example: Defining and Using Multiple Classes
Let’s write a second sample program to get more practice in defining classes. In
this example, we will define a new class named Account. An Account object has
the name of the owner (String) and the balance (double). We have two methods—
add and deduct—to deposit to and withdraw money from the account. There are
methods to set the initial balance and retrieve the current balance. These two
methods are named setInitialBalance and getCurrentBalance. Finally, we have
an accessor and mutator for the account owner’s name—getOwnerName and
setOwnerName.
wu23399_ch04.qxd 12/13/06 18:00 Page 156
The second sample program uses the Bicycle class from Section 4.1 and the
Account class we define shortly in this section. Here’s the second sample program:
4.2 Second Example:Defining and Using Multiple Classes 157
class SecondMain {
//This sample program uses both the Bicycle and Account classes
public static void main(String[] args) {
Bicycle bike;
Account acct;
String myName = Jon Java;
bike = new Bicycle( );
bike.setOwnerName(myName);
acct = new Account( );
acct.setOwnerName(myName);
acct.setInitialBalance(250.00);
acct.add(25.00);
acct.deduct(50);
//Output some information
System.out.println(bike.getOwnerName() +  owns a bicycle and);
System.out.println(has $  + acct.getCurrentBalance() +
 left in the bank);
}
}
This program creates one Bicycle object and one Account object, sets their
owner name to Jon Java, initializes the account balance to $250.00, adds $25.00 to
the account, deducts $50.00 from the account, and finally prints out some informa-
tion of bike and acct objects. The program diagram is as follows:
SecondMain
Bicycle
Account
wu23399_ch04.qxd 12/13/06 18:00 Page 157
We are using the Bicycle class from Section 4.1 without modification, so we
only have to consider defining the Account class. There are two data members for
the class, one to store the owner’s name and another to maintain the account bal-
ance. We have the following declaration for the two data members:
class Account {
private String ownerName;
private double balance;
//constructor and method declarations come here
}
The set and get methods for the owner’s name are identical to those defined
for the Bicycle class. The add and deduct methods modifiy the balance by adding or
deducting the passed amount. They are defined as follows:
public void add(double amt) {
balance = balance + amt;
}
public void deduct(double amt) {
balance = balance - amt;
}
The setInitialBalance and getCurrentBalance methods are similarly defined as the
other set and get methods. Here’s the complete definition of the Account class:
158 Chapter 4 Defining Your Own Classes—Part 1
class Account {
// Data Members
private String ownerName;
private double balance;
//Constructor
public Account( ) {
ownerName = Unassigned;
balance = 0.0;
}
//Adds the passed amount to the balance
public void add(double amt) {
balance = balance + amt;
}
//Deducts the passed amount from the balance
public void deduct(double amt) {
balance = balance - amt;
}
wu23399_ch04.qxd 12/13/06 18:00 Page 158
Figure 4.3 shows a class diagram of the Account class.
The second sample program is composed of three classes (we are not count-
ing the standard classes).
We need to compile the three classes before we can run the program. However, we
do not have to compile all three classes every time we want to run the program. For
example, if the Bicycle class is already compiled and we are not making any changes
to it, then there’s no need to compile the class again. (Note: We are assuming here
that both programs are placed in the same directory. If the second program is in a
separate folder, then you need to copy the bytecode file Bicycle.class to this folder.)
Notice the second call to the deduct method from the main method of
SecondMain, which is
acct.deduct(10);
but the parameter for the deduct method is declared as type double. This call is valid
because we are passing a value that is assignment-compatible to the double data
type. We will elaborate on this topic in Section 4.3.
SecondMain.java
SecondMain Program
Bicycle.java Account.java
4.2 Second Example:Defining and Using Multiple Classes 159
//Returns the current balance of this account
public double getCurrentBalance( ) {
return balance;
}
//Returns the name of this account's owner
public String getOwnerName( ) {
return ownerName;
}
//Sets the initial balance of this account
public void setInitialBalance(double bal) {
balance = bal;
}
//Assigns the name of this account's owner
public void setOwnerName(String name) {
ownerName = name;
}
}
wu23399_ch04.qxd 12/13/06 18:00 Page 159
160 Chapter 4 Defining Your Own Classes—Part 1
Figure 4.3 A class diagram of the Account class with two data members,one constructor,and six methods.
Account
balance
ownerName
Account( )
add( double )
deduct( double )
getCurrentBalance( )
getOwnerName( )
setInitialBalance( double )
setOwnerName( String )
1. What is the output from the following code fragment?
Account acct;
acct = new Account( );
acct.setInitialBalance(250);
acct.add(20);
System.out.println(Balance: 
+ acct.getCurrentBalance());
2. Write a code fragment to declare and create two Account objects named acc1
and acct2. Initialize the balance to $300 and $500, respectively. Set the name
of owner for both accounts to John Doe.
4.3 Matching Arguments and Parameters
Consider the following sample class that includes a method named compute. This
method has three parameters—two int and one double.
class Demo {
...
public void compute(int i, int j, double x) {
//method body
//the actual statements in the body
//are irrelevant to the discussion
}
...
}
wu23399_ch04.qxd 12/13/06 18:00 Page 160
When we call the compute method, we must pass three values. The values we
pass must be assignment-compatible with the corresponding parameters. For exam-
ple, it is not okay to pass a double value to an int parameter. Here are some valid
calls from the main method:
class MyMain {
public static void main(String[] arg) {
Demo demo = new Demo();
int i, k, m;
i = 12;
k = 10;
m = 14;
demo.compute(3, 4, 5.5);
demo.compute(i, k, m);
demo.compute(m, 20, 40);
}
}
In the statement
demo.compute(m, 20, 40);
the values m, 20, and 40 are called arguments. An argument is a value we pass to a
method, and the value is assigned to the corresponding parameters. A parameter
is a placeholder in the called method to hold the value of a passed argument. The ar-
guments and parameters are matched in left-to-right order. As long as the data
type of an argument is assignment-compatible to the corresponding parameter, the
call is valid.
The identifier we use for an argument has no relation to the identifier used for
the corresponding parameter. In the statement
demo.compute(i, k, m);
the fact that the same identifier i is used for both the first parameter and the first ar-
gument has no significance. They are two distinct and separate variables, as shown
in Figure 4.4. The figure also shows how the matching is done.
4.3 Matching Arguments and Parameters 161
argument
parameter
A parameter receives the value of a corresponding argument.Because a
parameter is like a placeholder that will not hold a value until an argument is
passed to it,a parameter is called a formal parameter and an argument an
actual parameter.
wu23399_ch04.qxd 12/13/06 18:00 Page 161
4.4 Passing Objects to a Method
When calling the methods of the Bicycle and Account classes, we passed a numer-
ical value or a String. In this section, we study how to pass an object when calling
a method. Since a String is an object, in a sense, we actually know to pass an ob-
ject as an argument to a method. However, a String is treated much as a primitive
datum for the most part, so we will cover this topic using instances of our own
class.
First, we define the Student class. A Student object has a name (String) and an
email (String). Here’s the definition:
162 Chapter 4 Defining Your Own Classes—Part 1
class Demo {
public void compute(int i, int j, double x) {
...
}
}
Demo demo = new Demo( );
int i = 5;
int k = 14;
demo.compute( i, k, 20 );
Passing side
Receiving side
5
i
Passing side Receiving side
Memory Allocation
14
k
20
5
i
14
j
20.0
x
This is a literal
constant so it
has no name.
Figure 4.4 This diagram illustrates how the argument values are assigned,or passed,to the matching
parameters.
class Student {
//Data Members
private String name;
private string email;
wu23399_ch04.qxd 12/13/06 18:00 Page 162
Then we define the LibraryCard class. A LibraryCard object is owned by a
Student, and it records the number of books being checked out. Here’s the definition:
4.4 Passing Objects to a Method 163
//Constructor
public Student( ) {
name = Unassigned;
email = Unassigned;
}
//Returns the email of this student
public String getEmail( ) {
return email;
}
//Returns the name of this student
public String getName( ) {
return name;
}
//Assigns the email of this student
public void setEmail(String address) {
email = address;
}
//Assigns the name of this student
public void setName(String studentName) {
name = studentName;
}
}
Student
name
email
Student( )
getEmail( )
getName( )
setEmail( String )
setName( String )
class LibraryCard {
// Data Members
//student owner of this card
private Student owner;
//number of books borrowed
private int borrowCnt;
//Constructor
public LibraryCard( ) {
owner = null;
borrowCnt = 0;
}
LibraryCard
owner
borrowCnt
LibraryCard( )
checkOut( int )
getNumberOfBooks( )
getOwnerName( )
setOwner( Student )
toString( )
wu23399_ch04.qxd 12/13/06 18:00 Page 163
Notice that we initialize the data member owner to null in the constructor. The value
of null means that owner is pointing to no object. The setOwner method must be
called to assign a Student object. The method accepts a Student object as its para-
meter and sets the data member owner to this Student object.
The getOwnerName method returns the name of the owner. It is defined as
public String getOwnerName( ) {
return owner.getName( );
}
Because the data member owner refers to a Student object, we can get the name of
this student by calling its getName method.
The toString method is a method that returns a string representation of an ob-
ject. Because an object can have a nested structure (e.g., an object’s data member
points to an instance of another class, the data members of this instance point to
instances of other classes, and so forth), it is convenient for those who use the class
to have a quick way to get printable information of an instance. Without such a
toString method, the programmer who uses the class must write a code to fetch the
164 Chapter 4 Defining Your Own Classes—Part 1
//numOfBooks are checked out
public void checkOut(int numOfBooks) {
borrowCnt = borrowCnt + numOfBooks;
}
//Returns the number of books borrowed
public int getNumberOfBooks( ) {
return borrowCnt;
}
//Returns the name of the owner of this card
public String getOwnerName( ) {
return owner.getName( );
}
//Sets owner of this card to student
public void setOwner(Student student) {
owner = student;
}
//Returns the string representation of this card
public String toString( ) {
return Owner Name:  + owner.getName( ) + n +
 Email:  + owner.getEmail( ) + n +
Books Borrowed:  + borrowCnt;
}
}
wu23399_ch04.qxd 12/13/06 18:00 Page 164
values of the data members individually. This can be quite tedious. With the
toString method, she can display information of an instance by calling just one
method toString.
The power of being able to pass an object to a method comes in handy when
we want multiple objects to share the same object. For example, suppose a single
student owns two library cards (say, one for the general library and another for the
engineering library). Then we can make the data member owner of two LibraryCard
objects to refer to the same Student object. Here’s one such program:
4.4 Passing Objects to a Method 165
class Librarian {
public static void main(String[] args) {
Student student;
LibraryCard card1, card2;
student = new Student( );
student.setName(Jon Java);
student.setEmail(jj@javauniv.edu);
card1 = new LibraryCard( );
card1.setOwner(student);
card1.checkOut(3);
card2 = new LibraryCard( );
card2.setOwner(student); //the same student is the owner
//of the second card, too
System.out.println(Card1 Info:);
System.out.println(card1.toString() + n);
System.out.println(Card2 Info:);
System.out.println(card2.toString() + n);
}
}
In this program, we create one Student object. Then we create two LibraryCard
objects. For each of these LibraryCard objects, we pass the same student when call-
ing their setOwner methods:
card1.setOwner(student);
...
card2.setOwner(student);
After the setOwner method of card2 is called in the main method, we have the
state of memory as shown in Figure 4.5.
wu23399_ch04.qxd 1/12/07 10:43 Page 165
It is critical to realize that when we say pass an object to a method, we are not
sending a copy of an object, but rather a reference to the object. Figure 4.6 shows
how the passing of an object is done.
166 Chapter 4 Defining Your Own Classes—Part 1
Figure 4.5 The state where the data members of two objects (of LibraryCard) are pointing to the same
object (of Student).
card1
:LibraryCard
student
owner
3
borrowCnt
card2
:LibraryCard
owner
0
borrowCnt
“Jon Java”
:Student
name
“jj@javauniv.edu”
email
When we pass an object to a method,we are actually passing the address,
or reference,of an object to the method.
It is possible to return the Student object itself by defining the following method:
public Student getOwner( ) {
return owner;
}
We will discuss such a method that returns an instance of a programmer-defined
class in Chapter 7.
wu23399_ch04.qxd 12/13/06 18:00 Page 166
4.5 Constructors
We provide more detailed coverage of the constructors in this section. The con-
structors we have defined so far accept no arguments. These constructors set the
data members to some initial values. For example, the constructor for the Bicycle
class in Section 4.1 initializes the value of owner (String) to Unassigned. For this
particular Bicycle class, such a simplistic constructor is adequate. However, most
cases require the constructors that accept one or more arguments. In fact, the way
we defined the constructor for the Account class in Section 4.2 could lead to poten-
tial problems. In this section, we describe the use of constructors that accept one
or more arguments, and we show how this solves the potential problems of the
Account class.
Let’s begin by reviewing the Account class from Section 4.2. We will identify
some potential problems and present a new constructor as a solution to rectify them.
4.5 Constructors 167
Figure 4.6 This diagram illustrates how an object is passed as an argument to a method.
class LibraryCard {
public void setOwner(Student student) {
owner = student;
}
}
LibraryCard card2;
card2 = new LibraryCard( );
card2.setOwner(student);
Passing side
Receiving side
Memory Allocation
1
2
Passing side Receiving side
For an object, the
content of a
variable is an
address, and this
address is passed
to the method.
1
2
student student
:LibraryCard
owner
0
borrowCnt
“Jon Java”
:Student
name
“jj@javauniv.edu”
email
card2
wu23399_ch04.qxd 12/13/06 18:00 Page 167
Consider the following code:
Account acct;
acct = new Account( );
acct.setInitialBalance(500);
acct.setInitialBalance(300);
What is the effect of such code? It is logically inconsistent to initialize the starting
balance more than once. It should be called exactly one, but there is no such Java
language feature that puts constraints on the number of times the setInitialBalance
method can be called. The existence of this method is a problem, and we can remove
it from the Account class by defining a constructor that sets the initial balance to a
specified amount.
Now consider the following code:
Account acct;
acct = new Account( );
acct.add(200.00);
If an account can have the initial balance of zero, this code is acceptable. But if there
isarulethatsays,forexample,anaccountmusthavetheinitialbalanceof$25ormore,
then the setInitialBalance method must be called first to initialize the balance to 25.00
or more before any transactions (add or deduct) take place. This problem can also be
solved by the same constructor that sets the initial balance to a specified amount.
Here’s a new constructor that eliminates the two problems in one stroke:
public Account(double startingBalance) {
ownerName = Unassigned;
balance = startingBalance;
}
Once this constructor is defined, there is no longer a need for the setInitialBalance
method, so we can safely remove it from the class defintion. Only the add and
deduct methods affect the balance after an object is created.
After the old constructor is replaced by this new constructor, we must create
an instance by passing one argument when calling the new operator. For example,
the code
Account acct;
acct = new Account(500.00);
will create a new Account object with its starting balance set to $500. We can no
longer create an instance by writing
Account acct;
acct = new Account( );
because there is no matching constructor anymore.
168 Chapter 4 Defining Your Own Classes—Part 1
wu23399_ch04.qxd 12/13/06 18:00 Page 168
Instead of this one-parameter constructor, we can define a constructor that
accepts the name of the owner also, so that it, too, can be initialized at the time of
object creation. Here’s how we define the two-parameter constructor:
public Account(String name, double startingBalance) {
ownerName = name;
balance = startingBalance;
}
This is the constructor we will include in the modified Account class. With this two-
parameter constructor, here’s how we create an Account object:
Account acct;
acct = new Account(John Smith, 500.00);
Notice that, even with this new constructor, we will keep the setOwnerName
method in the class because we want to be able to change the name of the owner
after the account is created.
From the three different constructors possible for the Account class, we have
selected the two-parameter constructor to include in the class. Actually, it is possi-
ble to include all three constructors in the definition of the Account class. But until
we learn how to define multiple constructors in Chapter 7, we will define exactly
one constructor for our programmer-defined classes.
4.5 Constructors 169
It is possible to define more than one constructor to a class. Multiple contructors
are called overloaded constructors. It is almost always a good idea to define multi-
ple constructors to a class. But to keep things simple, we will manage with one
constructor per class until Chapter 7.
We are now ready to list the complete definition. Here’s the second version of
the Account class (for the actual class name we will use AccountVer2 to avoid con-
fusion when discussing different versions of the class definition):
class AccountVer2 {
// Data Members
private String ownerName;
private double balance;
//Constructor
public AccountVer2(String name, double startingBalance) {
wu23399_ch04.qxd 12/13/06 18:00 Page 169
Default Constructor
As a design guideline, we strongly recommend to include constructors to
programmer-defined classes, as we have been doing from the beginning of the
chapter. However, it is not a requirement to define a constructor explicitly in a
class. If no constructor is defined for a class, then the Java compiler will auto-
matically include a default constructor. A default constructor is a constructor that
accepts no arguments and has no statements in its body. For example, if we omit
a constructor from the Bicycle class, a default constructor
public Bicycle( ) {
}
will be added to the class by the compiler to ensure its instances can be created.
Even though a default constructor is automatically added by the compiler, we
should never rely on it. We should always define our own constructor so that we can
170 Chapter 4 Defining Your Own Classes—Part 1
ownerName = name;
balance = startingBalance;
}
//Adds the passed amount to the balance
public void add(double amt) {
balance = balance + amt;
}
//Deducts the passed amount from the balance
public void deduct(double amt) {
balance = balance - amt;
}
//Returns the current balance of this account
public double getCurrentBalance( ) {
return balance;
}
//Returns the name of this account's owner
public String getOwnerName( ) {
return ownerName;
}
//Assigns the name of this account's owner
public void setOwnerName(String name) {
ownerName = name;
}
}
default
constructor
wu23399_ch04.qxd 12/13/06 18:00 Page 170
initialize the data members properly and carry out any other initialization tasks.
This ensures an object is created in a valid state (such as setting the balance of an
account to more than the minimum).
4.5 Constructors 171
Always define a constructor and initialize data members fully in the
constructor so an object will be created in a valid state.
Once we define our own constructor, no default constructor is added. This means
that once the constructor, such as
public Account(String name, double startingBalance ) {
ownerName = name;
balance = startingBalance;
}
is added to the Account class, we will no longer be able to create a Account object
anymore by executing
Account acct;
acct = new Account( );
because no matching constructor can be found in the class.
Once a programmer has added an explicitly defined constructor to a class,no
default constructor will be added to the class by the compiler.
1. Which of the following constructors are invalid?
public int ClassA(int one) {
...
}
public ClassB(int one, int two) {
...
}
void ClassC( ) {
...
}
wu23399_ch04.qxd 12/13/06 18:00 Page 171
2. What is the main purpose of a constructor?
3. Complete the following constructor.
class Test {
private double score;
public Test(double val) {
//assign the value of parameter to
//the data member
}
}
4.6 Information Hiding and Visibility Modifiers
The modifiers public and private designate the accessibility, or visibility, of data
members and methods. Although it is valid in Java, we do not recommend that pro-
grammers, especially beginners, leave out the visibility modifier in declaring data
members and methods. From the object-oriented design standpoint, we recommend
that you always designate the data members and methods as private or public. We
explain how to use these modifiers in this section. But before we get into the details,
we first discuss the object-oriented design philosophy behind these modifiers.
Consider a mobile robot as an example. What kind of behavior do we expect
from a mobile robot? Behaviors such as moving forward, turning, stopping, and
changing speed come to mind easily. When we define a class, say, MobileRobot, we
will include public methods such as move, turn, stop, and changeSpeed. These
methods are declared public so the programmers who use a MobileRobot object can
call these methods from their programs. We call these programmers client program-
mers and their programs client programs.
Now let’s assume that the move method accepts an integer argument as a dis-
tance to travel in meters. Suppose this mobile robot has three wheels with a motor
attached to each of the left and right rear wheels. The robot has no steering mecha-
nism, so the turning is done by rotating the left and right rear wheels at different
speeds. For example, by rotating the left wheel faster than the right wheel, the robot
will make a gradual left turn. To move forward, the robot must send the same
amount of power to the two motors. While the motors are rotating, the robot must
constantly monitor the distance traveled and stop the motors when the designated
distance is traveled.
The MobileRobot class includes methods such as rotate to rotate the motor
and readDistance to read the distance traveled. These methods are declared private
because they are internal details that need to be hidden from the client program-
mers. From our perspective as a client programmer, all we care is that the mobile
robot exhibits the behavior of moving the desired distance when we call its move
method. We do not care what’s going on inside. This is called information hiding. It
is not our concern how many motors the robot has or what type of mechanism is
employed to move the robot. We say the mobile robot encapsulates the internal
workings.
172 Chapter 4 Defining Your Own Classes—Part 1
client
programmers
information
hiding
encapsulation
wu23399_ch04.qxd 12/13/06 18:00 Page 172
This encapsulation mechanism allows easier modification of program code.
For example, suppose the motion mechanism of a mobile robot is modified to a sin-
gle motor and rack-and-pinion steering. Both wheels are now connected to a single
axle, and the motor turns this axle (via gears). The internal mechanism has changed,
but this will not affect the client programs. Calling the move method still exhibits
the same behavior.
To implement its methods (both public and private), the MobileRobot class
will necessarily include many data members, such as current speed, current direc-
tion, power levels of the motors, and so forth. These data members are internal de-
tails of the class because it is not a concern of the client programmers to know
which and how many of them are defined in the class. As such, data members are
declared as private.
In summary, behavior of the instances is implemented by public methods,
while the internal details that must be hidden from the client programmers are im-
plemented by private methods and private data members.
4.6 Information Hiding and Visibility Modifiers 173
Public methods of a class determine the behavior of its instances.Internal
details are implemented by private methods and private data members.
Now let’s go through a concrete example to see what would happen if some-
thing that should be an internal detail is declared public. To illustrate why declaring
data members public is considered a bad design, let’s consider the AccountVer2
class. Suppose its data member balance is declared as public:
class AccountVer2 {
public double balance;
//the rest is the same
}
Moving a mobile robot forward in reality is actually a far more difficult task than
described in the text. First, applying the same power to the two motors does not
guarantee the straight movement due to the difference in the motor characteris-
tics and the floor condition.Second,the robot needs to carry out some form of ob-
stacle avoidance, using a device such as a sonar or infrared sensor, because we
normally do not want a robot to crash into a wall. Third, stopping is not achieved
by abruptly shutting off the power to the motors. This will make the stopping
too sudden. We want to gradually reduce the power level so the robot comes to
a smooth stop. And there are other complexities involved in actually moving a
physical robot.
wu23399_ch04.qxd 12/13/06 18:00 Page 173
If this were the class definition, we could not prohibit client programmers
from writing code such as
AccountVer2 myAcct;
myAcct = new AccountVer2(John Smith, 300.00);
myAcct.balance = 670.00;
This breaks the AccountVer2 class because the balance can be modified directly
by the client programmers. The purpose of removing the setInitialBalance method is
defeated because the client programmers will have direct access to the data member
balance. They can change its value as they wish. If the instance variable balance is
properly hidden by declaring it private, then the client programmers cannot modify its
value directly. They can update the value indirectly only via the add and deduct meth-
ods. This maintains the integrity of the class, because the values of the data members
are changed only via the public methods the class designer provides. The client pro-
grammers cannot access or modify the data members through the back door.
174 Chapter 4 Defining Your Own Classes—Part 1
Declaring the data members private ensures the integrity of the class.
To distingush the private and public components of a class in the program di-
agram, we use the plus symbol () for public and the minus symbol () for private.
Using these symbols, the diagram that shows both data members and methods for
the AccountVer2 class becomes
AccountVer2
 balance
 ownerName
 AccountVer2( String, double )
 add( double )
 deduct( double )
 getCurrentBalance( )
 getOwnerName( )
 setOwnerName( String )
1. If the data member speed is private, is the following statement valid in a
client program?
Robot aibo;
aibo = new Robot();
double currentSpeed = aibo.speed;
wu23399_ch04.qxd 12/13/06 18:00 Page 174
2. Suppose you wrote down important information, such as your bank account
number, student registration ID, and so forth, on a single sheet of paper. Will
this sheet be declared private and kept in your desk drawer, or public and
placed next to the dorm’s public telephone?
3. Identify the private methods from the following diagram.
4.7 Class Constants
We introduced the use of the reserved final in declaring constants in Section 3.3.
The constants we declared there were used by only one method—the main method.
In this section we will show how a class constant is declared. A class constant will
be shared by all methods of the class.
Let’s define another version of the Account class (the actual name will be
AccountVer3). This time we will charge a fixed fee whenever a deduction is made.
Here’s how the class is declared (we will not list the unchanged methods here):
MyClass
 mydata : double
 MyClass( )
 methodOne(double) : void
 methodTwo(double) : double
 methodThree(double) : double
4.7 Class Constants 175
class AccountVer3 {
// Data Members
private static final double FEE = 0.50;
private String ownerName;
private double balance;
//Constructor
public AccountVer3(String name, double startingBalance) {
ownerName = name;
balance = startingBalance;
}
//Deducts the passed amount from the balance
public void deduct(double amt) {
balance = balance - amt - FEE;
}
Class constant
declaration
Fee is charged
every time
wu23399_ch04.qxd 12/13/06 18:00 Page 175
This is the output we get when we run the program:
Owner: Carl Smith
Bal : $18.50
Notice the use of a DecimalFormat object to display the result to two decimal places.
Here is the dependency relationship diagram (standard classes are not included)
DeductionWithFee AccountVer3
176 Chapter 4 Defining Your Own Classes—Part 1
//other methods are exactly the same as before, so
//we will omit them here
}
import java.text.*;
class DeductionWithFee {
//This sample program deducts money three times
//from the account
public static void main(String[] args) {
DecimalFormat df = new DecimalFormat(0.00);
AccountVer3 acct;
acct = new AccountVer3(Carl Smith, 50.00);
acct.deduct(10);
acct.deduct(10);
acct.deduct(10);
System.out.println(Owner:  + acct.getOwnerName());
System.out.println(Bal : $
+ df.format(acct.getCurrentBalance()));
}
}
The following sample program shows that the fee of $1.50 is charged after
three deductions.
wu23399_ch04.qxd 12/13/06 18:00 Page 176
Bad Version
and the source files for the program are
The class constant FEE is declared as
private static final double FEE = 0.50;
The modifier final designates that the identifier FEE is a constant, and the modifier
static designates that it is a class constant. The reserved word static is used to de-
clare class components, such as class variables and class methods. The inclusion of
the reserved word static in the declaration of the main method indicates that it is a
class method. It is not so frequent that we use class variables and class methods
(except, of course, the main method), and we will not be seeing their examples until
later in the book.
Before we move to another example, consider the following (problematic)
declaration:
class AccountVer3 {
private final double FEE = 0.50;
//the rest is the same
}
This declaration is not an error, but it is inefficient. If FEE is declared as a class con-
stant, then there will be one copy for the class, and this single copy is shared by all in-
stances of the class. If FEE is declared without the static modifier, then it is an instance
constant. This means every instance of the class will have its own copy of the same
value. For example, instead of one copy of the value 0.50, there will be 100 copies of
the same value 0.50 if there are 100 instances of the class. So, to make effective use
of a memory, when we declare a data member as a constant, it should be declared as
a class constant. This problem was introduced in Chapter 1, and Figure 1.9 illustrates
the problem.
DeductionWithFee.java AccountVer3.java
4.7 Class Constants 177
If a data member is a constant,declare it as a class constant.
wu23399_ch04.qxd 12/13/06 18:00 Page 177
Let’s try another sample program. This time we will write a class that models
a die. Notice how the constants are used in the following Die class:
178 Chapter 4 Defining Your Own Classes—Part 1
class Die {
//Data Members
//the largest number on a die
private static final int MAX_NUMBER = 6;
//the smallest number on a die
private static final int MIN_NUMBER = 1;
//To represent a die that is not yet rolled
private static final int NO_NUMBER = 0;
private int number;
//Constructor
public Die( ) {
number = NO_NUMBER;
}
//Rolls the die
public void roll( ) {
number = (int) (Math.floor(Math.random() *
(MAX_NUMBER - MIN_NUMBER + 1)) + MIN_NUMBER);
}
//Returns the number on this die
public int getNumber( ) {
return number;
}
}
We use the instance variable number to store the value of a die after it is rolled.
Inside the constructor, we initialize number to the constant NO_NUMBER to repre-
sent the state before the die is rolled. The roll method uses the formula for random
number generation described in Chapter 3. The minimum and the maximum
numbers on a die are kept as the class constants. By changing their values, our soft-
ware die can be made to represent any range of values, not just 1 to 6. (Note: Yes,
we can change their values when we edit the class. A Java constant only means
that we cannot change its value while the program is running.)
wu23399_ch04.qxd 12/13/06 18:00 Page 178
Here’s a program that uses three Die objects to simulate a roll of three dice:
4.7 Class Constants 179
class RollDice {
//Simulates the rolling of three dice
public static void main(String[] args) {
Die one, two, three;
one = new Die( );
two = new Die( );
three = new Die( );
one.roll();
two.roll();
three.roll();
System.out.println(Results are  + one.getNumber( ) +   +
two.getNumber( ) +   +
three.getNumber( ) );
}
}
The dependency diagram and a sample output are as follows:
Results are 3 6 5
The output of this program is rather primitive, but it still conveys the neces-
sary information. We will learn some drawing techniques in Chapter 5, so we can
really draw the image of three dice.
Let’s adapt the implemention of the Die class to write another program.
Here’s the scenario for our next program. Getting a single-occupancy room in a
dormitory is very tough because of high demand. There’s one especially large and
comfortable single-occupancy room in your dorm that everybody covets. The
housing office runs a lottery at the beginning of a quarter. Students must submit
RollDice Die
wu23399_ch04.qxd 12/13/06 18:00 Page 179
their entries before the lottery (if there’s no winner, then the room will be auc-
tioned off at eBay). The result of the lottery will consist of three cards. The num-
bers on a card range from 10 to 15, and the color of a card can be red, green, or
blue. Here are some possible outcomes:
We will write a program that will select a winning combination of lottery
cards. Following the implementation style of the Die class, we will define a class
that models the lottery card. There will be two instance variables, one for color and
another for the number. We will use a random number generator to select a color and
a number for each lottery card. To represent a color, we will use a simple coding:
1 for red, 2 for green, and 3 for blue.
Here’s the LotteryCard class:
13 13 13 draw 1
draw 2
13 10 15
12 11 10 draw 3
180 Chapter 4 Defining Your Own Classes—Part 1
class LotteryCard {
// Data Members
//the largest number on a card
private static final int MAX_NUMBER = 15;
//the smallest number on a card
private static final int MIN_NUMBER = 10;
//to represent a card before drawing
private static final int NO_NUMBER = 0;
//the 'largest' color for a card
private static final int MAX_COLOR = 3;
//the 'smallest' color for a card
private static final int MIN_COLOR = 1;
//to represent a card before drawing
private static final int NO_COLOR = 0;
//selected number on this card
private int number;
//selected color of this card
private int color;
wu23399_ch04.qxd 12/13/06 18:00 Page 180
4.7 Class Constants 181
//Constructor
public LotteryCard( ) {
number = NO_NUMBER;
color = NO_COLOR;
}
//spin the card
public void spin( ) {
number = (int) (Math.floor(Math.random()
* (MAX_NUMBER - MIN_NUMBER + 1)) + MIN_NUMBER);
color = (int) (Math.floor(Math.random()
* (MAX_COLOR - MIN_COLOR + 1)) + MIN_COLOR);
}
//Returns the number on this card
public int getNumber( ) {
return number;
}
//Returns the color of this card
public int getColor( ) {
return color;
}
}
And here’s the main class that draws the winning card combination:
class RoomWinner {
//Simulates the rolling of three dice
public static void main(String[] args) {
LotteryCard one, two, three;
one = new LotteryCard( );
two = new LotteryCard( );
three = new LotteryCard( );
one.spin();
two.spin();
three.spin();
System.out.println(Winning Card Combination: );
System.out.println(1 - red; 2 - green; 3 - blue);
System.out.println( );
wu23399_ch04.qxd 12/13/06 18:00 Page 181
The dependency diagram is as follows:
When this program is executed, output similar to the following is displayed:
Winning Card Combination:
1 - red; 2 - green; 3 - blue
Color number
Card 1: 2 13
Card 2: 2 12
Card 3: 1 14
Again, the output is rather primitive. We will learn some drawing techniques in
Chapter 5 so we can draw the image of a card in the appropriate color.
Public Constants
We stated in Section 4.6 that data members should be declared private to ensure the
integrity of a class. Following this guideline, we declared the class constant data
members in both sample programs as private. But there is an exception. We may
want to declare certain types of class constants as public. Here are the reasons for
this exception. First, a constant is “read only” by its nature, so it won’t have a neg-
ative impact if we declare it as public. Second, a constant is a clean way to make
certain characteristics of the instances known to the client programs.
RoomWinner LotteryCard
182 Chapter 4 Defining Your Own Classes—Part 1
System.out.println( color number);
System.out.println(Card 1:  + one.getColor( )
+   + one.getNumber( ));
System.out.println(Card 2:  + two.getColor( )
+   + two.getNumber( ));
System.out.println(Card 3:  + three.getColor( )
+   + three.getNumber( ));
}
}
wu23399_ch04.qxd 12/13/06 18:00 Page 182
For example, if we want to make the amount of a fee public knowledge
(which is a good idea, because consumers need to know such information), we
make the class constant public as follows:
class AccountVer3 {
public static final double FEE = 0.50;
...
}
A client program can then access this information directly as
System.out.println(Fee charged per deduction is $ 
+ AccountVer3.FEE);
Notice that the class data members are accessed by the syntax
class name . class data members
The use of public class constants is quite common in Java, and we will be seeing
many examples of it in the later sample programs.
4.8 Local Variables 183
1. Declare two class constants named MIN_BALANCE and MAX_BALANCE whose
data types are double.
2. Is there any problem with the following declarations?
class Question {
private final int MAX = 20;
...
}
3. Modify the Die class so its instances will generate a number between 5 and 15,
inclusively.
4.8 Local Variables
We often need to use temporary variables while completing a task in a method.
Consider the deduct method of the Account class:
public void deduct(double amt) {
balance = balance - amt;
}
We can rewrite the method, using a local variable, as follows:
public void deduct(double amt) {
double newBalance; This is a local
variable
wu23399_ch04.qxd 12/13/06 18:00 Page 183
newBalance = balance - amt;
balance = newBalance;
}
The variable newBalance is called a local variable. They are declared within
the method declaration and used for temporary purposes, such as storing intermedi-
ate results of a computation.
Such two-step assignment to update the current balance may not seem so use-
ful here, but consider a situation in which we need to check for certain conditions
before actually changing the value of currentBalance. For example, we may want to
disallow the purchase if the balance goes below a preset minimum balance. So if
newBalance becomes lower than the set minimum, then we’ll leave balance un-
changed. If we don’t use any local variable, then we have to deduct the amount from
balance (temporarily) and change it back to the previous amount. Use of a tempo-
rary local variable will result in a much cleaner code. We will see how such check-
ing is done in Chapter 5 using a selection statement.
The methods in the sample classes from this chapter are still very short, so the
useoflocalvariablesmaynotbeclear-cut.However,wewillwitnessanincreaseinthe
use of local variables in the coming chapters when the methods become complex.
While the data members of a class are accessible from all instance methods of
the class, local variables and parameters are accessible only from the method in which
they are declared, and they are available only while the method is being executed.
Memory space for local variables and parameters is allocated upon declaration and at
the beginning of the method, respectively, and erased upon exiting from the method.
184 Chapter 4 Defining Your Own Classes—Part 1
local variable
Local variables and parameters are erased when the execution of a method is
completed.
When you declare a local variable, make sure the identifier you use for it does
not conflict with the data members of a class. Consider the following hypothetical
class declaration:
class Sample {
private int number;
...
public void doSomething( ) {
int number;
number = 15;
}
...
}
The same identifier is used for both the
local variable and the instance variable.
This changes the value
of the local variable,not
the instance variable.
wu23399_ch04.qxd 12/13/06 18:00 Page 184
This class declaration is not an error. It is acceptable to use the same identifier for a
local variable, but it is not advisable. The following association rules are used:
4.9 Calling Methods of the Same Class 185
Rules for associating an identifier to a local variable,a parameter,and a data
member:
1. If there’s a matching local variable declaration or a parameter,then the
identifier refers to the local variable or the parameter.
2. Otherwise,if there’s a matching data member declaration,then the identifier
refers to the data member.
3. Otherwise,it is an error because there’s no matching declaration.
So the assignment
number = 15;
will change the value of the local variable. This may or may not be the intent of the
programmer. Even if this is the programmer’s intention, it is cleaner and easier to
read, especially to other programmers, to use different identifiers for local variables.
Avoid using the same identifier for the local variables and the data members of a
class.
1. How is a local variable different from an instance variable?
2. Rewrite the following method, using local variables.
public int totalCharge(int amt) {
return (balance -
(int) Math.round(amt * 1.5));
}
4.9 Calling Methods of the Same Class
Up until now, whenever we called a method of some object, we used dot notation,
such as acct.deduct(12). Just as we can call a method of another object, it is possi-
ble to call a method from a method of the same object. Figure 4.7 illustrates the
wu23399_ch04.qxd 12/13/06 18:00 Page 185
difference between calling another method of the same object and calling a method
of a different object.
Let’s look at a few examples. In the first example, we modify the AccountVer3
class so the add and deduct methods call the private method adjust. Here’s how the
modified class is declared (the actual class name is AccountVer4, and only the rele-
vant portion is listed here):
186 Chapter 4 Defining Your Own Classes—Part 1
:AClass
public void myMethod(){
BClass obj
= new BClass();
obj.doWork();
}
:BClass
public void doWork(){
...
}
public void myMethod(){
doWork();
}
Dot notation is optional
when you are calling a
method of the same object.
Dot notation is necessary
when you are calling a
method of another object.
Figure 4.7 The difference between calling a method belonging to the same object and a method belonging
to a different object.
class AccountVer4 {
...
//Adds the passed amount to the balance
public void add(double amt) {
adjust(amt);
}
//Deducts the passed amount from the balance
public void deduct(double amt) {
adjust( -(amt+FEE) );
}
...
//Adjusts the account balance
private void adjust(double adjustAmt) {
balance = balance + adjustAmt;
}
}
wu23399_ch04.qxd 12/13/06 18:00 Page 186
The add and deduct methods differ only in whether you add to or subtract the
amount from the balance. In the modified class, we redefine the two methods so
they call the common private method adjust. This method adds the passed amount
to the balance (in the case for the deduct method, we pass the negative amount be-
cause adding a negative value X is equivalent to subtracting a positive value X).
Here’s how the add method is defined:
public void add(double amt) {
adjust(amt);
}
Notice there is no dot notation.This is calling another method that belongs
to the same class.
When we call a method that belongs to the same class, we just include the method
name, as follows:
adjust(amt);
No dot notation is necessary.
4.9 Calling Methods of the Same Class 187
No dot notation is necessary when you call a method from another method of the
same class.
Let’s look at the second example. In the original Die class, when a new in-
stance was created, we set its number to NO_NUMBER. This means if a programmer
calls the getNumber method of a Die object before calling its roll method, she will
get NO_NUMBER as a result. For a real die, there’s no such NO_NUMBER state, so
instead of instantiating a new Die object in such a state, we’ll redefine the class so a
die gets rolled when it is first created. The trick here is to call the roll method from
the constructor. Here’s how the modified Die class is declared (the class name is
DieVer2):
class DieVer2 {
//Data Members
//the largest number on a die
private static final int MAX_NUMBER = 6;
wu23399_ch04.qxd 12/13/06 18:00 Page 187
The constructor simply calls the roll method. So when a new Die object is cre-
ated, a number is already preselected. Notice that it is possible to declare the
constructor as
public DieVer2( ) {
number = (int) (Math.floor(Math.random()
* (MAX_NUMBER - MIN_NUMBER + 1))
+ MIN_NUMBER);
}
But this ends up duplicating the same code. Instead of repeating the same code in
the class, it is much better organizationally to define a single method and call this
method from multiple places. Duplication of code, in general, makes the modifica-
tion of code tedious and error-prone. Imagine the situation in which the same code
is repeated at 10 different locations. It is so easy to miss one or two of them at the
modification time.
188 Chapter 4 Defining Your Own Classes—Part 1
//the smallest number on a die
private static final int MIN_NUMBER = 1;
private int number;
//Constructor
public DieVer2( ) {
roll();
}
//Rolls the die
public void roll( ) {
number = (int) (Math.floor(Math.random()
* (MAX_NUMBER - MIN_NUMBER + 1)) + MIN_NUMBER);
}
//Returns the number on this die
public int getNumber( ) {
return number;
}
}
Avoid duplicating the same code.Duplication of code often means tedious and
error-prone activities when you modify the code.
wu23399_ch04.qxd 12/13/06 18:00 Page 188
4.10 Changing Any Class to a Main Class
In this section, we will show you a simple way to make any class (such as Bicycle)
also the main class of a program. Instead of defining a separate main class, as we
have done so far, it is possible to define the main method to a class so the class be-
comes the main class of a program also. There are a number of advantages in doing
this. First, we have one less class to manage if we don’t have to define a separate
main class. This advantage may not be seem so substantial. However, when we
write numerous classes (e.g., writing solutions to the chapter exercises), writing a
separate main class for all those classes so they become executable becomes te-
dious. Second, when we develop reusable classes (such as Die and Account) for
other programmers, we often want to include a simple example on how to use the
classes. Instead of providing a separate sample main class, it is more convenient to
add the main method to these classes.
We illustrate the procedure, using the Bicycle class from Section 4.1. Suppose
we want to show a sample use of this class. Instead of creating a separate sample main
class, we can define the main method to the Bicycle class. Here’s the Bicycle class that
is also a main class:
class Bicycle {
// Data Member
private String ownerName;
//Returns the name of this bicycle's owner
public String getOwnerName( ) {
return ownerName;
}
//Assigns the name of this bicycle's owner
public void setOwnerName(String name) {
ownerName = name;
}
//The main method that shows a sample
//use of the Bicycle class
public static void main(String[] args) {
Bicycle myBike;
myBike = new Bicycle( );
4.10 Changing Any Class to a Main Class 189
1. Suppose a class Alpha includes a method called compute that accepts no
arguments. Define another method of Alpha named myMethod that calls the
compute method.
2. Why should duplication of code be avoided?
wu23399_ch04.qxd 12/13/06 18:00 Page 189
myBike.setOwnerName(Jon Java);
System.out.println(myBike.getOwnerName() +
owns a bicycle);
}
}
Remember that the new Bicycle class having the main method does not pro-
hibit us from defining a separate main class. All Java requires us to do is to include
the main method to the classes we designate as the main class of the program. So it
is possible (although not likely) that every class in the program has the main
method, and we can select one of them to be the main class when we execute the
program. We will use this technique whenever appropriate in the textbook, begin-
ning with this chapter’s sample development section.
190 Chapter 4 Defining Your Own Classes—Part 1
Any class can include the main method.For a program to be executable,the desig-
nated main class must include the main method.Other classes in the program may
or may not include the main method.It is irrelevant to the execution of the program.
program
tasks
Loan Calculator
In Chapter 3, we wrote a loan calculator program that computes the monthly and total
payments for a given loan amount, loan period, and interest rate.We wrote the program
using the simplified program structure in which we had one main class with one method
(main). We will implement the program again, but this time we use classes called Loan
and LoanCalculator.
Problem Statement
The problem statement is the same as that in Chapter 3. We repeat the statement to
refresh your memory:
Write a loan calculator program that computes both monthly and total pay-
ments for a given loan amount,annual interest rate,and loan period.
Overall Plan
The tasks we identified in Chapter 3 for the program are still the same:
1. Get three input values:loanAmount,interestRate, and loanPeriod.
2. Compute the monthly and total payments.
3. Output the results.
Sample Development
4.11 Sample Development
wu23399_ch04.qxd 12/13/06 18:00 Page 190
The main difference in this implementation lies in the use of additional classes. In-
stead of building the program by using only the main class and performing all the tasks
in one big main method, we will define two classes Loan and LoanCalculator. An
instance of the LoanCalculator class acts as a top-level agent that manages all other
objects in the program, such as Loan and Scanner. The Loan class captures the logic of
loan calculation. A single instance of the Loan class represents a loan, so if the program
deals with five loans, for example, then five Loan objects will be created in the program.
We will make the LoanCalculator class the main class of the program by adding the main
method to it.Figure 4.8 shows the program diagram.
Notice that the roles that LoanCalculator and Loan play in the program are quite
different.The Loan class is a generic class that provides a service (i.e.,loan computation
and currency conversion) and is intended to be reused by different programs. The
LoanCalculator class, on the other hand, is a class designed specifically for this pro-
gram, so the class is not intended for reuse by other programs. It is important to recog-
nize this distinction because the way we design reusable and nonreusable classes is
quite different. We call the class that provides some type of service a service provider
and the class that manages other classes and objects in a program a controller. In gen-
eral, a service provider is designed as a reusable class, while a controller is designed as
a nonreusable class.
What would be the development steps for this program? If we have multiple
classes to implement, we can develop the program in either a top-down or a bottom-
up manner. With the top-down development, we develop in a kind of outside-in
fashion. We develop the top-level controller class first. But to test its functionalities
fully, we need the service objects it uses. In a top-down development, we use tempo-
rary dummy service objects that return a fake value from their methods. After we
verify that the controller class is working correctly, we then complete the service class
with the real methods. The top-down development for this program will implement
the LoanCalculator class first with the dummy Loan class and then the real Loan
class.
4.11 Sample Development 191
Scanner
LoanCalculator
Loan
Figure 4.8 The program diagram for the LoanCalculator program.
service
provider
controller
top-down
develop-
ment
wu23399_ch04.qxd 12/13/06 18:00 Page 191
4.11 Sample Development—continued
192 Chapter 4 Defining Your Own Classes—Part 1
develop-
ment steps
step 1
design
With the bottom-up development, we develop in the reverse inside-out fashion;
that is, we develop the service classes first.To test the service classes, we write a tempo-
rary dummy main class.After the service classes are done,we complete the top-level class
that uses these service classes.The bottom-up development for this program implements
the Loan class first fully and then the LoanCalculator class. For both approaches, the
classes are developed incrementally as usual.
For this sample development, we will adopt the top-down development. We will
leave the bottom-up development for this program as an exercise. For some sample ap-
plications in the later chapters,we will adopt the bottom-up development.We implement
this program in five steps:
1. Start a skeleton of the LoanCalculator class.The skeleton LoanCalculator class
will include only an object/variable declaration and a constructor to create
objects.Define a temporary placeholder Loan class.
2. Implement the input routine to accept three input values.
3. Implement the output routine to display the results.
4. Implement the computation routine to compute the monthly and total payments.
5. Finalize the program,implementing any remaining temporary methods and
adding necessary methods as appropriate.
Step 1 Development: Program Skeleton
Since the LoanCalculator object is the top-level agent of the program that manages
other objects,we need a method to create these objects.We do this in the constructor.We
define separate methods for input, computation, and output to organize the class more
logically. Designing a set of single-task methods is more manageable and easier to un-
derstand than having one method that performs all three tasks of input, computation,
and output.We will call the methods getInput, computePayment, and displayOutput.
We will also include one method called describeProgram that describes the purpose of
the program to the user.
Since an instance of the class is the top-level agent, much as a general contractor,
we will provide one method the programmer can call to control the whole operation.We
will name the method start and define it as follows:
public void start( ) {
describeProgram();
getInput();
computerPayment();
displayOutput();
}
bottom-up
develop-
ment
wu23399_ch04.qxd 12/13/06 18:00 Page 192
With this method,we can then call the main method as follows:
public static void main(String[] args){
LoanCalculator calculator = new LoanCalculator();
calculator.start();
}
It is possible to define the main method to make it call the four operation methods
(describeProgram, computePayment, getInput, and displayOutput) directly, elimi-
nating the need to define the start method. However, such organization limits the flexi-
bility and usability of the class.By defining the start method, if other programmers want
to use the LoanCalculator class in their programs, they need only call the start method.
Without the start method,they have to call the four methods and remember to call them
in the correct order. Although the difference is not dramatic in this particular case, it can
be in the cases when the classes are more complex and the number of classes in a pro-
gram is large.
Let’s summarize the methods we will define for the LoanCalculator class:
4.11 Sample Development 193
Design Document: The LoanCalculator Class
Method Visibility Purpose
start public Carries out the loan calculation by
calling the other private methods.
computePayment private Given three parameters—loan amount,
loan period, and interest rate—it com-
putes monthly and total payments.The
actual computation is done by a
Loan object.
describeProgram private Displays a short description of the
program.
displayOutput private Displays the result—monthly and total
payments.
getInput private Uses Scanner to get three input
values—loan amount, loan period, and
interest rate.
Notice that only the start method is public. We declare all other methods as private
because we do not want any client programmers to use them directly;we want the client
programmers to be able to call only the start method.
In this step, we define the four private methods with only a temporary output
statement inside the method body to verify that the methods are called correctly. A
method that has no “real” statements inside the method body is called a stub. The four
stub
wu23399_ch04.qxd 12/13/06 18:00 Page 193
4.11 Sample Development—continued
194 Chapter 4 Defining Your Own Classes—Part 1
methods are defined as follows:
private void describeProgram() {
System.out.println(inside describeProgram); //TEMP
}
private void getInput() {
System.out.println(inside getInput); //TEMP
}
private void computePayment() {
System.out.println(inside computePayment); //TEMP
}
private void displayOutput() {
System.out.println(inside displayOutput); //TEMP
}
Notice the comment marker //TEMP after the output statements. It is our convention to
attach this comment marker so we can easily and quickly locate temporary statements.
We use System.out for temporary output.
The purpose of the skeleton LoanCalculator class is to declare and create all the
necessary data members. At this step, we know of only one object that will be used by
LoanCalculator, namely,a Loan object.The declaration part of the LoanCalculator class
will be as follows:
class LoanCalculator {
private Loan loan;
...
}
At this point, the constructor for the LoanCalculator class is very simple.The only
data member is a Loan object,so we will create it in the constructor as follows:
public LoanCalculator( ) {
loan = new Loan( );
}
For this constructor to work properly,we need the definition for the Loan class.We begin
with the minimalist skeleton code for the Loan class:
class Loan {
public Loan( ) {
}
}
wu23399_ch04.qxd 12/13/06 18:00 Page 194
Let’s put our design in an actual code.The skeleton LoanCalculator class is defined
as follows.
4.11 Sample Development 195
step 1 code
/*
Chapter 4 Sample Development: Loan Calculation (Step 1)
File: Step1/LoanCalculator.java
*/
class LoanCalculator {
//Data members
private Loan loan;
//Main method
public static void main(String[] arg) {
LoanCalculator calculator = new LoanCalculator();
calculator.start();
}
//Constructor
public LoanCalculator() {
loan = new Loan();
}
// Top-level method that calls other private methods
public void start() {
describeProgram(); //tell what the program does
getInput(); //get three input values
computePayment(); //compute the monthly payment and total
displayOutput(); //display the results
}
// Computes the monthly and total loan payments
private void computePayment() {
System.out.println(inside computePayment); //TEMP
}
// Provides a brief explanation of the program to the user
private void describeProgram() {
System.out.println(inside describeProgram); //TEMP
}
// Displays the input values and monthly and total payments
private void displayOutput() {
System.out.println(inside displayOutput); //TEMP
}
wu23399_ch04.qxd 12/13/06 18:00 Page 195
4.11 Sample Development—continued
196 Chapter 4 Defining Your Own Classes—Part 1
// Gets three input values—loan amount, interest rate, and
// loan period—using an InputBox object
private void getInput() {
System.out.println(inside getInput); //TEMP
}
}
And finally the skeleton Loan class is defined as follows.
We run the step 1 program and verify that the following text appears in the standard out-
put window:
inside describeProgram
inside getInput
inside computePayment
inside displayOutput
After the step 1 program is compiled and executed correctly, we move on to
step 2.
Step 2 Development: Accept Input Values
In the second step of coding,we implement the getInput method.We will reuse the input
routine we derived in Chapter 3.When we receive three input values,we must pass these
values to the Loan object loan.We will add three data members to keep track of the three
step 2
design
step 1 test
/*
Chapter 4 Sample Development: Loan Calculation (Step 1)
File: Step1/Loan.java
*/
class Loan {
public Loan( ) {
}
}
wu23399_ch04.qxd 12/13/06 18:00 Page 196
input values and one constant to aid the conversion:
class Loan {
private static final int MONTHS_IN_YEAR = 12;
private double loanAmount;
private double monthlyInterestRate;
private int numberOfPayments;
...
}
Notice that the annual interest rate and loan period expressed in the number of years are
the input,but we are keeping monthly interest rate and the number of monthly payments
for the loan period to make them more compatible to the loan calculation formula we are
using.We need to define three set methods (mutators) for interest rate, loan period, and
loan amount. A set method for the number of payments, for example, can be defined as
follows:
public void setPeriod(int periodInYear) {
numberOfPayments = periodInYear * MONTHS_IN_YEAR;
}
We define a complementary set of accessor methods. The getPeriod method, for
example,is defined as
public int getPeriod( ) {
return (numberOfPayments / MONTHS_IN_YEAR);
}
Notice that the value returned by an accessor may or may not be the data member. It is
possible that the value returned is derived from the data member, as was the case with
the getLoanPeriod method.
We mentioned in Section 4.4 the importance of a constructor initializing an object
properly.Now that we have associated data members to the Loan class,let’s define a con-
structor that accepts arguments:
public Loan(double amount, double rate, int period) {
setAmount(amount);
setRate (rate );
setPeriod(period);
}
Having this updated Loan class, we are now ready to tackle the getInput method
of the LoanCalculator class.We perform the input routine as we did in the sample pro-
gram from Chapter 3:
Scanner scanner = new Scanner(System.in);
System.out.print(Loan Amount (Dollars+Cents): );
loanAmount = scanner.nextDouble();
4.11 Sample Development 197
wu23399_ch04.qxd 12/13/06 18:00 Page 197
4.11 Sample Development—continued
198 Chapter 4 Defining Your Own Classes—Part 1
System.out.print(Annual Interest Rate (e.g., 9.5): );
annualInterestRate = scanner.nextDouble();
System.out.print(Loan Period - # of years: );
loanPeriod = scanner.nextInt();
After getting three input values,we create a new Loan object as
loan = new Loan(loanAmount,
annualInterestRate,
loanPeriod);
Finally, we include test output statements to verify that the values are read in and
assigned to loan correctly:
System.out.println(Loan Amount: $
+ loan.getAmount());
System.out.println(Annual Interest Rate:
+ loan.getRate() + %);
System.out.println(Loan Period (years):
+ loan.getPeriod());
From this point on, to maintain a focus on the changes we are making, we show
only the portion where we made modifications or additions. Unchanged portions are
represented by three dots (. . .). Please refer to the actual source file for the viewing of
complete source code.Here’s the step 2 LoanCalculator class:
step 2 code
/*
Chapter 4 Sample Development: Loan Calculation (Step 2)
File: Step2/LoanCalculator.java
*/
import java.util.*;
class LoanCalculator {
. . .
public LoanCalculator() {
}
. . .
private void getInput(){
double loanAmount, annualInterestRate;
wu23399_ch04.qxd 12/13/06 18:00 Page 198
4.11 Sample Development 199
The step 2 Loan class is as follows:
int loanPeriod;
Scanner scanner = new Scanner(System.in);
System.out.print(Loan Amount (Dollars+Cents):);
loanAmount = scanner.nextDouble();
System.out.print(Annual Interest Rate (e.g., 9.5):);
annualInterestRate = scanner.nextDouble();
System.out.print(Loan Period - # of years:);
loanPeriod = scanner.nextInt();
//create a new loan with the input values
loan = new Loan(loanAmount, annualInterestRate,loanPeriod);
//TEMP
System.out.println(Loan Amount: $ + loan.getAmount());
System.out.println(Annual Interest Rate:
+ loan.getRate() + %);
System.out.println(Loan Period (years): + loan.getPeriod());
//TEMP
}
. . .
}
/*
Chapter 4 Sample Development: Loan Calculation (Step 2)
File: Step2/Loan.java
*/
class Loan {
private final int MONTHS_IN_YEAR = 12;
private double loanAmount;
private double monthlyInterestRate;
private int numberOfPayments;
wu23399_ch04.qxd 12/13/06 18:00 Page 199
4.11 Sample Development—continued
200 Chapter 4 Defining Your Own Classes—Part 1
//Constructor
public Loan(double amount, double rate, int period) {
setAmount(amount);
setRate (rate );
setPeriod(period);
}
//Returns the loan amount
public double getAmount( ) {
return loanAmount;
}
//Returns the loan period in number of years
public int getPeriod( ) {
return numberOfPayments / MONTHS_IN_YEAR;
}
//Returns the loan's annual interest rate
public double getRate( ) {
return monthlyInterestRate * 100.0 * MONTHS_IN_YEAR;
}
//Sets the loan amount
public void setAmount(double amount) {
loanAmount = amount;
}
//Sets the annual interest rate
public void setRate(double annualRate) {
monthlyInterestRate = annualRate / 100.0 / MONTHS_IN_YEAR;
}
//Sets the loan period
public void setPeriod(int periodInYears) {
numberOfPayments = periodInYears * MONTHS_IN_YEAR;
}
}
As before, to verify the input routine is working correctly, we run the program multiple
times. For each run, we enter a different set of data to verify that the values entered are
displayed correctly.
step 2 test
wu23399_ch04.qxd 12/13/06 18:01 Page 200
Step 3 Development: Output Values
In the third step of development, we implement the displayOutput method. We will
reuse the design of output layout from Chapter 3. The actual task of computing the
monthly and total payments is now delegated to the Loan class, so we will add two
methods—getMonthlyPayment and getTotalPayment—to the Loan class.The focus in
step 3 is the layout for output, so we will define a temporary dummy code for these two
methods in the following manner:
public double getMonthlyPayment( ) {
return 132.15; //TEMP
}
public double getTotalPayment( ) {
return 15858.10; //TEMP
}
To display the monthly and total payments, we add the following code in the
displayOutput method:
private void displayOutput( ) {
//echo print the input values here
System.out.println(Monthly payment is $  +
loan.getMonthlyPayment() );
System.out.println( TOTAL payment is $  +
loan.getTotalPayment() );
}
Notice that by defining the getMonthlyPayment and getTotalPayment methods in the
Loan class, the computePayment method of LoanCalculator becomes redundant and
no longer needed.So we will remove it in this step.
Here are the modified LoanCalculator and Loan classes:
4.11 Sample Development 201
step 3
design
step 3 code
/*
Chapter 4 Sample Development: Loan Calculation (Step 3)
File: Step3/LoanCalculator.java
*/
import java.util.*;
class LoanCalculator {
...
// computePayment method is removed from the source file
private void displayOutput() {
System.out.println(Loan Amount: $ + loan.getAmount());
wu23399_ch04.qxd 12/13/06 18:01 Page 201
4.11 Sample Development—continued
202 Chapter 4 Defining Your Own Classes—Part 1
System.out.println(Annual Interest Rate:
+ loan.getRate() + %);
System.out.println(Loan Period (years):  + loan.getPeriod());
System.out.println(Monthly payment is $  +
loan.getMonthlyPayment());
System.out.println( TOTAL payment is $  +
loan.getTotalPayment());
}
private void getInput() {
//same code but the temporary echo print statements
//are removed
}
}
/*
Chapter 4 Sample Development: Loan Calculation (Step 3)
File: Step3/Loan.java
*/
class Loan {
...
public double getMonthlyPayment( ) {
return 132.15; //TEMP
}
public double getTotalPayment( ) {
return 15858.10; //TEMP
}
...
}
To verify the output routine is working correctly, we run the program multiple times and
verify that the layout looks okay for different values.It is common for a programmer to run
the program several times before the layout looks clean on the screen.
step 3 test
wu23399_ch04.qxd 12/13/06 18:01 Page 202
Bad Version
Step 4 Development: Compute Loan Amount
In the fourth step of development,we replace the temporary getMonthlyPayment and
getTotalPayment methods with the final version. The changes are made only to the
Loan class.The other two classes remain the same.
Here’s one possible way to define the two methods:
private double monthlyPayment;
public double getMonthlyPayment( ) {
monthlyPayment = ...;
return monthlyPayment;
}
public double getTotalPayment( ) {
return monthlyPayment * numberOfPayments;
}
The idea is to use the value of the data member monthlyPayment set by the
getMonthlyPayment method in computing the total payment.This setup is problematic
because the getTotalPayment method will not work correctly unless getMonthly-
Payment is called first. It is considered a very poor design, and generally unacceptable,
to require the client programmer to call a collection of methods in a certain order.
We must define the two methods so they can be called in any order,not necessarily in the
order of getMonthlyPayment and getTotalPayment. The correct way here is to call
getMonthlyPayment from the getTotalPayment method:
private double getTotalPayment( ) {
double totalPayment;
totalPayment = getMonthlyPayment() * numberOfPayments;
return totalPayment;
}
With this approach the data member monthlyPayment is not necessary.
Here’s the updated Loan class:
4.11 Sample Development 203
step 4
design
step 4 code
/*
Chapter 4 Sample Development: Loan Calculation (Step 4)
File: Step4/Loan.java
*/
class Loan {
...
public double getMonthlyPayment( ) {
double monthlyPayment;
wu23399_ch04.qxd 12/13/06 18:01 Page 203
4.11 Sample Development—continued
204 Chapter 4 Defining Your Own Classes—Part 1
After the method is added to the class, we need to run the program through a
number of test data.As in Chapter 3,we made the assumption that the input values must
be valid, so we will only test the program for valid input values. For sample test data, we
repeat the table from Chapter 3.The right two columns show the correct results.Remem-
ber that these input values are only suggestions,not a complete list of test data.You must
try other input values as well.
monthlyPayment = (loanAmount * monthlyInterestRate)
/
(1 - Math.pow(1/(1 + monthlyInterestRate),
numberOfPayments ) );
return monthlyPayment;
}
public double getTotalPayment( ) {
double totalPayment;
totalPayment = getMonthlyPayment( ) * numberOfPayments;
return totalPayment;
}
...
}
step 4 test
Input
Output
(shown up to three decimal
places only)
Loan
Amount
Annual
Interest
Rate
Loan
Period
(in Years)
Monthly
Payment
Total
Payment
10000 10 10 132.151 15858.088
15000 7 15 134.824 24268.363
10000 12 10 143.471 17216.514
0 10 5 0.000 0.000
30 8.5 50 0.216 129.373
Step 5 Development: Finalize
Now in the last step of development, we finalize the class declaration by completing the
describeProgram method, the only method still undefined. We may give a very long
step 5
design
wu23399_ch04.qxd 12/13/06 18:01 Page 204
description or a very terse one.An ideal program will let the user decide.We do not know
how to write such code yet, so we will display a short description of the program using
System.out.
Another improvement is the display of monetary values in two decimal places.
We can format the display to two decimal places by using the DecimalFormat class as
explained in Chapter 3.
Here’s the describeProgram method;
private void describeProgram() {
System.out.println
(This program computes the monthly and total);
System.out.println
(payments for a given loan amount, annual );
System.out.println
(interest rate, and loan period (# of years).);
System.out.println(n);
}
You may feel that there’s not much testing we can do in this step. After all, we add only a
single method that carries out a simple output routine. However, many things can go
wrong between step 4 and step 5.You may have deleted some lines of code inadvertently.
You may have deleted a necessary file by mistake.Anything could happen.The point is to
test after every step of development to make sure everything is in order.
Summary 205
step 5 code
step 5 test
• Data members of a class refer to the instance and class variables and
constants of the class.
• An object’s properties are maintained by a set of data members.
• Class methods can access only the class variables and class constants.
• Instance methods can access all types of data members of the class.
• Public methods define the behavior of an object.
• Private methods and data members (except certain class constants) are
considered internal details of the class.
• Components (data members and methods) of a class with the visibility
modifier private cannot be accessed by the client programs.
• Components of a class with the visibility modifier public can be accessed by
the client programs.
• A method may or may not return a value. One that does not return a value is
called a void method.
S u m m a r y
wu23399_ch04.qxd 12/13/06 18:01 Page 205
• A constructor is a special method that is executed when a new object is
created. Its purpose is to initialize the object into a valid state.
• Memory space for local variables and parameters is allocated when a method
is called and deallocated when the method terminates.
• A public method that changes a property of an object is called a mutator.
• A public method that retrieves a property of an object is called an accessor.
• Dot notation is not used when you call a method from another method of the
same class.
• Any class can be set as the main class of a program by adding the main
method to it. In the main method, an instance of this class is created.
206 Chapter 4 Defining Your Own Classes—Part 1
K e y C o n c e p t s
programmer-defined classes
accessibility (visibility) modifiers
void methods
value-returning methods
accessors
mutators
constructors
arguments
parameters
client programmers
information hiding
encapsulation
local variables
service providers
controllers
stub
E x e r c i s e s
1. Consider the following class declaration.
class QuestionOne {
public final int A = 345;
public int b;
private float c;
private void methodOne( int a) {
b = a;
}
public float methodTwo( ) {
return 23;
}
}
wu23399_ch04.qxd 12/13/06 18:01 Page 206
Identify invalid statements in the following main class. For each invalid
statement, state why it is invalid.
class Q1Main {
public static void main(String[] args) {
QuestionOne q1;
q1 = new QuestionOne( );
q1.A = 12;
q1.b = 12;
q1.c = 12;
q1.methodOne(12);
q1.methodOne( );
System.out.println(q1.methodTwo(12));
q1.c = q1.methodTwo( );
}
}
2. What will be the output from the following code?
class Q2Main {
public static void main(String[] args) {
QuestionTwo q2;
q2 = new QuestionTwo( );
q2.init();
q2.increment();
q2.increment();
System.out.println(q2.getCount());
}
}
class QuestionTwo {
private int count;
public void init( ) {
count = 1;
}
public void increment( ) {
count = count + 1;
}
public int getCount( ) {
return count;
}
}
Exercises 207
wu23399_ch04.qxd 12/13/06 18:01 Page 207
3. What will be the output from the following code? Q3Main and
Question Three classes are the slightly modified version of Q2Main and
QuestionTwo.
class Q3Main {
public static void main(String[] args) {
QuestionThree q3;
q3 = new QuestionThree( );
q3.init();
q3.count = q3.increment() + q3.increment();
System.out.println(q3.increment());
}
}
class QuestionThree {
public int count;
public void init( ) {
count = 1;
}
public int increment( ) {
count = count + 1;
return count;
}
}
4. Is there any problem with the following class? Is the passing of an argument
to the private methods appropriate? Are the data members appropriate?
Explain.
208 Chapter 4 Defining Your Own Classes—Part 1
/*
Problem Question4
*/
class MyText {
private String word;
private String temp;
private int idx;
public String firstLetter( ) {
idx = 0;
return getLetter(word);
}
public String lastLetter( ) {
idx = word.length() - 1;
return getLetter(word);
}
wu23399_ch04.qxd 12/13/06 18:01 Page 208
5. In the RollDice program, we created three Die objects and rolled them once.
Rewrite the program so you will create only one Die object and roll it three
times.
6. Modify the Bicycle class so instead of assigning the name of an owner
(Student), you can assign the owner object itself. Model this new Bicycle
class after the LibraryCard class.
7. Extend the LibraryCard class by adding the expiration date as a new property
of a library card. Define the following four methods:
//sets the expiration date
public void setExpDate(GregorianCalendar date) {...}
//returns the expiration year
public int getExpYear( ) { ... }
//returns the expiration month
public int getExpMonth( ) { ... }
//returns the expiration day
public int getExpDay( ) { ... }
8. Write a program that displays the recommended weight (kg), given the user’s
age and height (cm). The formula for calculating the recommended weight is
recommendedWeight = (height - 100 + age / 10) * 0.90
Define a service class named Height and include an appropriate method for
getting a recommended weight of a designated height.
9. Write a program that computes the total ticket sales of a concert. There are
three types of seatings: A, B, and C. The program accepts the number of
tickets sold and the price of a ticket for each of the three types of seats. The
total sales are computed as follows:
totalSales = numberOfA_Seats * pricePerA_Seat +
numberOfB_Seats * pricePerB_Seat +
numberOfC_Seats * pricePerC_Seat;
Write this program, using only one class, the main class of the program.
10. Redo Exercise 9 by using a Seat class. An instance of the Seat class keeps
track of the ticket price for a given type of seat (A, B, or C).
Exercises 209
private String getLetter(String str) {
temp = str.substring(idx, idx+1);
return temp;
}
}
wu23399_ch04.qxd 12/13/06 18:01 Page 209
11. Write a program that computes the area of a circular region (the shaded area
in the diagram), given the radii of the inner and the outer circles, ri and ro,
respectively.
We compute the area of the circular region by subtracting the area of the
inner circle from the area of the outer circle. Define a Circle class that has
methods to compute the area and circumference. You set the circle’s radius
with the setRadius method or via a constructor.
12. Write a WeightConverter class. An instance of this class is created by
passing the gravity of an object relative to the Earth’s gravity (see
Exercise 12 on page 138). For example, the Moon’s gravity is
approximately 0.167 of the Earth’s gravity, so we create a
WeightConverter instance for the Moon as
WeightConverter moonWeight;
moonWeight = new WeightConverter( 0.167 );
To compute how much you weigh on the Moon, you pass your weight on
Earth to the convert method as
yourMoonWeight = moonWeight.convert( 160 );
Use this class and redo Exercise 12 on page 138.
Development Exercises
For the following exercises, use the incremental development methodology to
implement the program. For each exercise, identify the program tasks, create a
design document with class descriptions, and draw the program diagram. Map out
the development steps at the start. Present any design alternatives and justify your
selection. Be sure to perform adequate testing at the end of each development step.
13. Redo Exercise 26 on page 143, but this time define and use programmer-
defined classes.
ri ro
210 Chapter 4 Defining Your Own Classes—Part 1
wu23399_ch04.qxd 12/13/06 18:01 Page 210
14. Write a program that accepts the unit weight of a bag of coffee in pounds
and the number of bags sold and displays the total price of the sale,
computed as follows:
totalPrice = bagWeight * numberOfBags * pricePerLb;
totalPriceWithTax = totalPrice + totalPrice * taxrate;
Display the result in the following manner:
Number of bags sold: 32
Weight per bag: 5 lb
Price per pound: $5.99
Sales tax: 7.25%
Total price: $ 1027.88
Define and use a programmer-defined CoffeeBag class. Include class
constants for the price per pound and tax rate with the values $5.99 per
pound and 7.25 percent, respectively.
15. In the Turtle exercises from the earlier chapters, we dealt with only one
Turtle (e.g., see Exercise 28 on page 144). It is possible, however, to let
multiple turtles draw on a single drawing window. To associate multiple
turtles to a single drawing, we create an instance of TurtleDrawingWindow
and add turtles to it as follows:
TurtleDrawingWindow canvas = new TurtleDrawingWindow( );
Turtle winky, pinky, tinky;
//create turtles;
//pass Turtle.NO_DEFAULT_WINDOW as an argument so
//no default drawing window is attached to a turtle.
winky = new Turtle(Turtle.NO_DEFAULT_WINDOW);
pinky = new Turtle(Turtle.NO_DEFAULT_WINDOW);
tinky = new Turtle(Turtle.NO_DEFAULT_WINDOW);
//now add turtles to the drawing window
canvas.add( winky );
canvas.add( pinky );
canvas.add( tinky );
Ordinarily, when you start sending messages such as turn and move to a
Turtle, it will begin moving immediately. When you have only one Turtle, this
is fine. However, if you have multiple turtles and want them to start moving
at the same time, you have to first pause them, then give instructions, and
finally command them to start moving. Here’s the basic idea:
winky.pause( );
pinky.pause( );
tinky.pause( );
Exercises 211
Format to two
decimal places.
wu23399_ch04.qxd 12/13/06 18:01 Page 211
//give instructions to turtles here,
//e.g., pinky.move(50); etc.
//now let the turtles start moving
winky.start( );
pinky.start( );
tinky.start( );
Using these Turtle objects, draw the following three triangles:
Use a different pen color for each triangle. Run the same program without
pausing and describe what happens.
pinky draws this
triangle.
winky draws this
triangle.
tinky draws this
triangle.
212 Chapter 4 Defining Your Own Classes—Part 1
wu23399_ch04.qxd 12/13/06 18:02 Page 212
Selection Statements
O b j e c t i v e s
After you have read and studied this chapter,you should be able to
• Implement selection control in a program
using if statements.
• Implement selection control in a program
using switch statements.
• Write boolean expressions using relational and
boolean operators.
• Evaluate given boolean expressions correctly.
• Nest an if statement inside another if
statement’s then or else part correctly.
• Describe how objects are compared.
• Choose the appropriate selection control
statement for a given task.
• Define and use enumerated constants.
• Draw geometric shapes on a window.
213
5
wu23399_ch05.qxd 12/14/06 17:49 Page 213
ecisions, decisions, decisions. From the moment we are awake until the time
we go to sleep, we are making decisions. Should I eat cereal or toast? What
should I wear to school today? Should I eat at the cafeteria today? And so
forth. We make many of these decisions by evaluating some criteria. If the
number of students in line for registration seems long, then come back tomorrow
for another try. If today is Monday, Wednesday, or Friday, then eat lunch at the
cafeteria.
Computer programs are no different. Any practical computer program con-
tains many statements that make decisions. Often a course of action is determined
by evaluating some kind of a test (e.g., Is the remaining balance of a meal card
below the minimum?). Statements in programs are executed in sequence, which is
called sequential execution or sequential control flow. However, we can add
decision-making statements to a program to alter this control flow. For example,
we can add a statement that causes a portion of a program to be skipped if
an input value is greater than 100. Or we can add a statement to disallow the pur-
chase of food items if the balance of a meal card goes below a certain minimum.
The statement that alters the control flow is called a control statement. In this
chapter we describe some important control statements, called selection state-
ments. In Chapter 6 we will describe other control statements, called repetition
statements.
5.1 The if Statement
There are two versions of the if statement, called if–then–else and if–then. We begin
with the first version. Suppose we wish to enter a student’s test score and print out
the message You did not pass if the score is less than 70 and You did pass if the score
is 70 or higher. Here’s how we express this logic in Java:
Scanner scanner = new Scanner(System.in);
System.out.print(Enter test score: );
int testScore = scanner.nextInt();
if (testScore  70)
System.out.println(You did not pass);
else
System.out.println(You did pass);
We use an if statement to specify which block of code to execute. A block of code
may contain zero or more statements. Which block is executed depends on the
214 Chapter 5 Selection Statements
I n t r o d u c t i o n
D
sequential
execution
control
statement
if statement
This statement is
executed if testScore
is less than 70.
This statement is
executed if testScore
is 70 or higher.
wu23399_ch05.qxd 12/14/06 17:49 Page 214
result of evaluating a test condition, called a boolean expression. The if–then–else
statement follows this general format:
if ( boolean expression )
then block
else
else block
Figure 5.1 illustrates the correspondence between the if–then–else statement we
wrote and the general format.
The boolean expression is a conditional expression that is evaluated
to either true or false. For example, the following three expressions are all
conditional:
testScore  80
testScore * 2  350
30  w / (h * h)
The six relational operators we can use in conditional expressions are:
 // less than
= // less than or equal to
== // equal to
!= // not equal to
 // greater than
= // greater than or equal to
Here are some more examples:
a * a = c //true if a * a is less than or equal to c
x + y != z //true if x + y is not equal to z
a == b //true if a is equal to b
5.1 The if Statement 215
boolean
expression
if-then-else
syntax
relational
operators
testScore  70 )
if (
else
System.out.println(You did not pass);
System.out.println(You did pass);
Boolean Expression
The then Block
The else Block
Figure 5.1 Mapping of the sample if–then–else statement to the general format.
wu23399_ch05.qxd 12/14/06 17:49 Page 215
If the boolean expression evaluates to true, then the statements in the then
block are executed. Otherwise, the statements in the else block are executed.
We will cover more complex boolean expressions in Section 5.2. Notice that we can
reverse the relational operator and switch the then and else blocks to derive the
equivalent code, for example,
if (testScore = 70)
System.out.println(You did pass);
else
System.out.println(You did not pass);
Notice that the reverse of  is =, not .
The if statement is called a selection or branching statement because it selects
(or branches to) one of the alternative blocks for execution. In our example, either
System.out.println(You did not pass);
or
System.out.println(You did pass);
is executed depending on the value of the boolean expression. We can illustrate a
branching path of execution with the diagram shown in Figure 5.2.
216 Chapter 5 Selection Statements
One very common error in writing programs is to mix up the assignment and
equality operators. We frequently make the mistake of writing
if (x = 5) ...
when we actually wanted to say
if (x == 5) ...
selection
statement
testScore  70?
System.out.println
(You did not pass);
System.out.println
(You did pass);
true
false
Figure 5.2 The diagram showing the control flow of the sample if–then–else statement.
wu23399_ch05.qxd 12/14/06 17:49 Page 216
In the preceding if statement, both blocks contain only one statement. The
then or else block can contain more than one statement. The general format for both
the then block and the else block is either a
single statement
or a
compound statement
where single statement is a Java statement and compound statement is a
sequence of Java statements surrounded by braces, as shown below with n  0
statements:
{
statement 1
statement 2
...
statement n
}
If multiple statements are needed in the then block or the else block, they must
be surrounded by braces { and }. For example, suppose we want to print out addi-
tional messages for each case. Let’s say we also want to print Keep up the good
work when the student passes and print Try harder next time when the student fails.
Here’s how:
5.1 The if Statement 217
Compound
Statements
if (testScore  70)
{
System.out.println(You did not pass);
System.out.println(Try harder next time);
}
else
{
System.out.println(You did pass);
System.out.println(Keep up the good work);
}
The braces are necessary to delineate the statements inside the block. Without
the braces, the compiler will not be able to tell whether a statement is part of the
block or part of the statement that follows the if statement.
Notice the absence of semicolons after the right braces. A semicolon is never
necessary immediately after a right brace. A compound statement may contain zero
wu23399_ch05.qxd 12/14/06 17:49 Page 217
or more statements, so it is perfectly valid for a compound statement to include only
one statement. Indeed, we can write the sample if statement as
if (testScore  70)
{
System.out.println(You did not pass);
}
else
{
System.out.println(You did pass);
}
Although it is not required, many programmers prefer to use the syntax for the com-
pound statement even if the then or else block includes only one statement. In this
textbook, we use the syntax for the compound statement regardless of the number
of statements inside the then and else blocks. Following this policy is beneficial for
a number of reasons. One is the ease of adding temporary output statements inside
the blocks. Frequently, we want to include a temporary output statement to verify
that the boolean expression is written correctly. Suppose we add output statements
such as these:
if (testScore  70)
{
System.out.println(inside then:  + testScore);
System.out.println(You did not pass);
}
else
{
System.out.println(inside else:  + testScore);
System.out.println(You did pass);
}
If we always use the syntax for the compound statement, we just add and delete
the temporary output statements. However, if we use the syntax of the single state-
ment, then we have to remember to add the braces when we want to include a
temporary output statement. Another reason for using the compound statement
syntax exclusively is to avoid the dangling else problem. We discuss this problem
in Section 5.2.
The placement of left and right braces does not matter to the compiler. The
compiler will not complain if you write the earlier if statement as
if (testScore  70)
{ System.out.println(You did not pass);
System.out.println(Try harder next time);} else
{
System.out.println(You did pass);
System.out.println(Keep up the good work);}
218 Chapter 5 Selection Statements
wu23399_ch05.qxd 12/14/06 17:49 Page 218
However, to keep your code readable and easy to follow, you should format
your if statements using one of the two most common styles:
if ( boolean expression ) {
...
} else {
...
}
if ( boolean expression )
{
...
}
else
{
...
}
In this book, we will use style 1, mainly because this style adheres to the code con-
ventions for the Java programming language. If you prefer style 2, then go ahead
and use it. Whichever style you choose, be consistent, because a consistent look and
feel is very important to make your code readable.
5.1 The if Statement 219
Style 1
Style 2
The document that provides the details of code conventions for Java can be found at
http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html
This document describes the Java language coding standards dictated in the Java
Language Specification.It is important to follow the code conventions as closely as
possible to increase the readability of the software.
There is a second variation of style 1 in which we place the reserved word else
on a new line as
if ( boolean expression ) {
...
}
else {
...
}
Many programmers prefer this variation of style 1 because the reserved word else
aligns with the matching if. However, if we nitpick, style 3 goes against the logic
behind the recommended style 1 format, which is to begin a new statement at one
position with a reserved word. The reserved word else is a part of the if statement,
not the beginning of a new statement. Thus style 1 places the reserved word else to
the right of the matching if.
Style 3
wu23399_ch05.qxd 12/14/06 17:49 Page 219
Again, the actual format is not that important. Consistent use of the same for-
mat is. So, whichever style you use, use it consistently. To promote consistency
among all programmers, we recommend that everybody to adopt the code conven-
tions. Even though the recommended format may look peculiar at first, with some
repeated use, the format becomes natural in no time.
Let’s summarize the key points to remember:
220 Chapter 5 Selection Statements
Rules for writing the then and else blocks:
1. Left and right braces are necessary to surround the statements if the then or
else block contains multiple statements.
2. Braces are not necessary if the then or else block contains only one statement.
3. A semicolon is not necessary after a right brace.
Now let’s study a second version of the if statement called if–then. Suppose
we want to print out the message You are an honor student if the test score is 95 or
above and print out nothing otherwise. For this type of testing, we use the second
version of the if statement, whose general format is
if ( boolean expression )
then block
The second version contains only the then block. Using this version and the
compound statement syntax, we express the selection control as
if (testScore = 95) {
System.out.println(You are an honor student);
}
Figure 5.3 shows the diagram that illustrates the control flow for this if–then state-
ment. We will refer collectively to both versions as the if statement.
Notice that the if–then statement is not necessary, because we can write any
if–then statement using if–then–else by including no statement in the else block. For
instance, the sample if–then statement can be written as
if (testScore = 95) {
System.out.println(You are an honor student);
} else { }
In this book, we use if–then statements whenever appropriate.
Let’s conclude this section with a sample class that models a circle. We
will name the class Ch5Circle, and its instances are capable of computing the
if-then syntax
wu23399_ch05.qxd 12/14/06 17:49 Page 220
circumference and area. We will include a test in this class so the methods such
as getArea and getCircumference return the constant INVALID_DIMENSION when
the dimension of the radius is invalid. Here’s the Ch5Circle class (most comments
are removed for the sake of brevity):
5.1 The if Statement 221
Figure 5.3 The diagram showing the control flow of the second version of the if statement.
System.out.println
(You are an honor student);
true
false
testScore = 95?
/*
Chapter 5 The Circle class
File: Ch5Circle.java
*/
class Ch5Circle {
public static final int INVALID_DIMENSION = -1;
private double radius;
public Ch5Circle(double r) {
setRadius(r);
}
public double getArea( ) {
double result = INVALID_DIMENSION;
if (isRadiusValid()) {
result = Math.PI * radius * radius;
}
return result;
}
As the number of methods gets larger,
we will use this marker to quickly locate
the program components.Shaded icon
is used for a private element.
Data Members
getArea
wu23399_ch05.qxd 12/14/06 17:49 Page 221
public double getCircumference( ) {
double result = INVALID_DIMENSION;
if (isRadiusValid()) {
result = 2.0 * Math.PI * radius;
}
return result;
}
public double getDiameter( ) {
double diameter = INVALID_DIMENSION;
if (isRadiusValid()) {
diameter = 2.0 * radius;
}
return diameter;
}
public double getRadius( ) {
return radius;
}
public void setDiameter(double d) {
if (d  0) {
setRadius(d/2.0);
} else {
setRadius(INVALID_DIMENSION);
}
}
public void setRadius(double r) {
if (r  0) {
radius = r;
} else {
radius = INVALID_DIMENSION;
}
}
private boolean isRadiusValid( ) {
return radius != INVALID_DIMENSION;
}
}
222 Chapter 5 Selection Statements
getDiameter
getRadius
setDiameter
setRadius
isRadiusValid
getCircumference
wu23399_ch05.qxd 12/14/06 17:49 Page 222
Notice the if statement in the getArea method is written as
if (isRadiusValid()) {
...
}
The boolean expression in the if statement can be any expression that evaluates
to true or false, including a call to a method whose return type is boolean,
such as the isRadiusValid method. The use of such a boolean method often
makes the code easier to read, and easier to modify if the boolean method is called
from many methods (e.g., there are three methods calling the isRadiusValid
method).
Here’s a short main class to test the functionality of the Ch5Circle class:
5.1 The if Statement 223
/*
Chapter 5 Sample Program: Computing Circle Dimensions
File: Ch5Sample1.java
*/
import java.util.*;
class Ch5Sample1 {
public static void main(String[] args) {
double radius, circumference, area;
Ch5Circle circle;
Scanner scanner = new Scanner(System.in);
System.out.print(Enter radius: );
radius = scanner.nextDouble();
circle = new Ch5Circle(radius);
circumference = circle.getCircumference();
area = circle.getArea();
System.out.println(Input radius:  + radius);
System.out.println(Circumference:  + circumference);
System.out.println(Area:  + area);
}
}
wu23399_ch05.qxd 12/14/06 17:49 Page 223
Notice that the program will display -1.0 when the input radius is invalid. We
can improve the display by adding an if test in the main program as follows:
System.out.print(Circumference: );
if (circumference == Ch5Circle.INVALID_DIMENSION) {
System.out.println(Cannot compute. Input invalid);
} else {
System.out.println(circumference);
}
Another possible improvement in the main program is to check the input value first.
For instance,
radius = ... ;
if (radius  0) {
//do the computation as the sample main method
} else {
//print out the error message
}
Even when a client programmer does not include appropriate tests in his program,
we must define a reusable class in a robust manner so it will not crash or produce
erroneous results. For the Ch5Circle class, we add a test so the data member radius
is set to either a valid datum or a specially designated value (INVALID_DIMENSION)
for any invalid data. By designing the class in this manner, we protect the class from
a possible misuse (e.g., attempting to assign a negative radius) and producing mean-
ingless results, such as –5.88. We always strive for a reliable and robust reusable
class that will withstand the abuse and misuse of client programmers.
224 Chapter 5 Selection Statements
1. Identify the invalid if statements:
a. if ( a  b ) then
x = y;
else
x = z;
b. if ( a  b )
else x = y;
c. if ( a  b )
x = y;
else {
x = z;
};
d. if ( a  b ) {
x = y; } else
x = z;
2. Are the following two if statements equivalent?
/*A*/ if ( x  y )
System.out.println(Hello);
else
System.out.println(Bye);
/*B*/ if ( x  y )
System.out.println(Bye);
else
System.out.println(Hello);
wu23399_ch05.qxd 12/14/06 17:49 Page 224
5.2 Nested if Statements
The then and else blocks of an if statement can contain any statement including
another if statement. An if statement that contains another if statement in either its
then or else block is called a nested if statement. Let’s look at an example. In the
earlier example, we printed out the messages You did pass or You did not pass
depending on the test score. Let’s modify the code to print out three possible mes-
sages. If the test score is lower than 70, then we print You did not pass, as before. If
the test score is 70 or higher, then we will check the student’s age. If the age is less
than 10, we will print You did a great job. Otherwise, we will print You did pass,
as before. Figure 5.4 is a diagram showing the logic of this nested test. The code
is written as follows:
if (testScore = 70) {
if (studentAge  10) {
System.out.println(You did a great job);
} else {
System.out.println(You did pass);//test score = 70
} //and age = 10
} else { //test score  70
System.out.println(You did not pass);
}
5.2 Nested if Statements 225
nested if
statement
Figure 5.4 A diagram showing the control flow of the example nested if statement.
System.out.println
(You did a great job);
System.out.println
(You did pass);
true
false
studentAge  10?
System.out.println
(You did not pass);
true
false
testScore = 70?
Another if
statement in the
then block of
the outer if
wu23399_ch05.qxd 12/14/06 17:49 Page 225
Since the then block of the outer if contains another if statement, the
outer if is called a nested if statement. It is possible to write if tests in different
ways to achieve the same result. For example, the preceding code can also be
expressed as
if (testScore = 70  studentAge  10) {
System.out.println(You did a great job);
} else {
//either testScore  70 OR studentAge = 10
if (testScore = 70) {
System.out.println(You did pass);
} else {
System.out.println(You did not pass);
}
}
Several other variations can also achieve the same result.As a general rule, we
strive to select the one that is most readable (i.e., most easily understood) and most
efficient. Often no one variation stands out, and the one you choose depends on your
preferred style of programming.
Here’s an example in which one variation is clearly a better choice. Suppose
we input three integers and determine how many of them are negative. Here’s
the first variation. To show the structure more clearly, we purposely do not use the
braces in the then and else blocks.
if (num1  0)
if (num2  0)
if (num3  0)
negativeCount = 3; //all three are negative
else
negativeCount = 2; //num1 and num2 are negative
else
if (num3  0)
negativeCount = 2; //num1 and num3 are negative
else
negativeCount = 1; //num1 is negative
else
if (num2  0)
if (num3  0)
negativeCount = 2; //num2 and num3 are negative
else
negativeCount = 1; //num2 is negative
else
if (num3  0)
negativeCount = 1; //num3 is negative
else
negativeCount = 0; //no negative numbers
226 Chapter 5 Selection Statements
In this and the following
examples,we purposely
do not use the braces so
we can provide a better
illustration of the topics
we are presenting.
wu23399_ch05.qxd 12/14/06 17:49 Page 226
It certainly did the job. But elegantly? Here’s the second variation:
negativeCount = 0;
if (num1  0)
negativeCount = negativeCount + 1;
if (num2  0)
negativeCount = negativeCount + 1;
if (num3  0)
negativeCount = negativeCount + 1;
Which version should we use? The second variation is the only reasonable
way to go. The first variation is not a viable option because it is very inefficient and
very difficult to read. We apply the nested if structure if we have to test conditions
in some required order. In this example these three tests are independent of one an-
other, so they can be executed in any order. In other words, it doesn’t matter whether
we test num1 first or last.
The statement
negativeCount = negativeCount + 1;
increments the variable by 1. This type of statement that changes the value of a vari-
able by adding a fixed number occurs frequently in programs. Instead of repeating
the same variable name twice, we can write it succinctly as
negativeCount++;
Similarly, a statement such as
count = count - 1;
can be written as
count--;
The double plus operator () is called the increment operator, and the double
minus operator () is the decrement operator (which decrements the variable
by 1). The increment and decrement operators have higher precedence than unary
operators. See Table 5.3 on page 235. Note: There are prefix and postfix increment/
decrement operators in which the operators come before and after the variable (or an
expression), respectively. We only use the postfix operators in this book, and the
precedence rules presented in the table apply to the postfix operators only.
Notice that we indent the then and else blocks to show the nested structure
clearly. Indentation is used as a visual guide for the readers. It makes no difference to
a Java compiler. For example, we make our intent clear by writing the statement as
if (x  y)
if (z != w)
a = b + 1;
else
a = c + 1;
else
a = b * c;
5.2 Nested if Statements 227
increment and
decrement
operators
wu23399_ch05.qxd 12/14/06 17:49 Page 227
228 Chapter 5 Selection Statements
It takes some practice before you can write well-formed if statements. Here are
some rules to help you write the if statements.
Rule 1: Minimize the number of nestings.
Rule 2: Avoid complex boolean expressions. Make them as simple as possible.
Don’t include many ANDs and ORs.
Rule 3: Eliminate any unnecessary comparisons.
Rule 4: Don’t be satisfied with the first correct statement. Always look for
improvement.
Rule 5: Read your code again.Can you follow the statement easily? If not,try to
improve it.
Test Score Grade
90  score A
80  score  90 B
70  score  80 C
60  score  70 D
score  60 F
The statement can be written as
if (score = 90)
System.out.println(Your grade is A);
else
if (score = 80)
System.out.println(Your grade is B);
else
if (score = 70)
System.out.println(Your grade is C);
But to the Java compiler, it does not matter if we write the same code as
if (x  y)if (z != w)a = b + 1;else a = c + 1; else a = b * c;
Although indentation is not required to run the program, using proper inden-
tation is an important aspect of good programming style. Since the goal is to make
your code readable, not to follow any one style of indentation, you are free to
choose your own style. We recommend style 1 shown on page 219.
The next example shows a style of indentation accepted as standard for a
nested if statement in which nesting occurs only in the else block. Instead of deter-
mining whether a student passes or not, we will now display a letter grade based on
the following formula:
wu23399_ch05.qxd 12/14/06 17:49 Page 228
else
if (score = 60)
System.out.println(Your grade is D);
else
System.out.println(Your grade is F);
However, the standard way to indent the statement is as follows:
if (score = 90)
System.out.println(Your grade is A);
else if (score = 80)
System.out.println(Your grade is B);
else if (score = 70)
System.out.println(Your grade is C);
else if (score = 60)
System.out.println(Your grade is D);
else
System.out.println(Your grade is F);
We mentioned that indentation is meant for human eyes only. For example, we
can clearly see the intent of a programmer just by looking at the indentation when
we read
if (x  y)
if (x  z)
System.out.println(Hello);
else
System.out.println(Good bye);
A Java compiler, however, will interpret the above as
if (x  y)
if (x  z)
System.out.println(Hello);
else
System.out.println(Good bye);
This example has a dangling else problem. The Java compiler matches an else
with the previous unmatched if, so the compiler will interpret the statement
by matching the else with the inner if ( if (x  z) ), whether you use indentation
style A or B. If you want to express the logic of indentation style A, you have to
express it as
if (x  y) {
if (x  z)
System.out.println(Hello);
} else
System.out.println(Good bye);
5.2 Nested if Statements 229
Indentation style A
Indentation style B
dangling else
problem
wu23399_ch05.qxd 12/14/06 17:49 Page 229
This dangling else problem is another reason why we recommend that beginners
use the syntax for compound statement in the then and else blocks. In other
words, always use the braces in the then and else blocks.
Let’s conclude this section by including tests inside the add and deduct methods
of the Account class from Chapter 4. For both methods, we will update the balance
only when the amount passed is positive. Furthermore, for the deduct method, we will
update the balance only if it does not become a negative number after the deduction.
This will require the use of a nested if statement.The following is the class declaration.
The name is Ch5Account, and this class is based on AccountVer2 from Chapter 4. We
only list the two methods here because other parts are the same as in AccountVer2.
230 Chapter 5 Selection Statements
class Ch5Account {
...
//Adds the passed amount to the balance
public void add(double amt) {
//add if amt is positive; otherwise, do nothing
if (amt  0) {
balance = balance + amt;
}
}
//Deducts the passed amount from the balance
public void deduct(double amt) {
//deduct if amt is positive; do nothing otherwise
if (amt  0) {
double newbalance = balance - amt;
if (newbalance = 0) { //if a new balance is positive, then
balance = newbalance; //update the balance; otherwise,
} //do nothing.
}
}
...
}
add
deduct
1. Rewrite the following nested if statements without using any nesting.
a. if ( a  c )
if ( b  c )
x = y;
else
x = z;
wu23399_ch05.qxd 12/14/06 17:49 Page 230
else
x = z;
b. if ( a == b )
x = y;
else
if ( a  b )
x = y;
else
x = z;
c. if ( a  b )
if ( a = b )
x = z;
else
x = y;
else
x = z;
2. Format the following if statements with indentation.
a. if ( a  b ) if ( c  d ) x = y;
else x = z;
b. if ( a  b ) { if ( c  d ) x = y; }
else x = z;
c. if ( a  b ) x = y; if ( a  c ) x = z;
else if ( c  d ) z = y;
5.3 Boolean Expressions and Variables
In addition to the arithmetic operators introduced in Chapter 3 and relational oper-
ators introduced in Section 5.2, boolean expressions can contain conditional and
boolean operators. A boolean operator, also called a logical operator, takes boolean
values as its operands and returns a boolean value. Three boolean operators are
AND, OR, and NOT. In Java, the symbols , ||, and ! represent the AND, OR, and
NOT operators, respectively. Table 5.1 explains how these operators work.
The AND operation results in true only if both P and Q are true. The OR op-
eration results in true if either P or Q is true. The NOT operation is true if A is false
and is false if P is true. Combining boolean operators with relational and arithmetic
operators, we can come up with long boolean expressions such as
(x + 150) == y || x  y  !(y  z  z  x)
(x  y)  (a == b || a == c)
a != 0  b != 0  (a + b  10)
In Section 5.1 we stated that we can reverse the relational operator and switch
the then and else blocks to derive the equivalent code. For example,
if (age  0) {
System.out.println(Invalid age is entered);
} else {
System.out.println(Valid age is entered);
}
5.3 Boolean Expressions and Variables 231
boolean
operator
wu23399_ch05.qxd 12/14/06 17:49 Page 231
is equivalent to
if ( !(age  0) ) {
System.out.println(Valid age is entered);
} else {
System.out.println(Invalid age is entered);
}
which can be written more naturally as
if (age = 0) {
System.out.println(Valid age is entered);
} else {
System.out.println(Invalid age is entered);
}
Reversing the relational operator means negating the boolean expression.
In other words, !(age  0) is equivalent to (age = 0). Now, consider the following
if-then-else statement:
if (temperature = 65  distanceToDestination  2) {
System.out.println(Let's walk);
} else {
System.out.println(Let's drive);
}
If the temperature is greater than or equal to 65 degrees and the distance to
the destination is less than 2 mi., we walk. Otherwise (it’s too cold or too far
away), we drive. How do we reverse the if-then-else statement? We can rewrite
the statement by negating the boolean expression and switching the then and else
blocks as
if ( !(temperature = 65  distanceToDestination  2) ) {
System.out.println(Let's drive);
} else {
System.out.println(Let's walk);
}
232 Chapter 5 Selection Statements
Table
Table 5.1 Boolean operators and their meanings
P Q P  Q P | | Q !P
false false false false true
false true false true true
true false false true false
true true true true false
wu23399_ch05.qxd 12/14/06 17:49 Page 232
or more directly and naturally as
if (temperature  65 || distanceToDestination = 2) {
System.out.println(Let's drive);
} else {
System.out.println(Let's walk);
}
The expression
!(temperature = 65  distanceToDestination  2)
is equivalent to
!(temperature = 65) || !(distanceToDestination  2)
which, in turn, is equivalent to
(temperature  65 || distanceToDestination = 2)
The logical equivalence is derived by applying the following DeMorgan’s law:
Table 5.2 shows their equivalence.
Now consider the following expression:
x / y  z || y == 0
What will be the result if y is equal to 0? Easy, the result is true, many of you might
say. Actually a runtime error called an arithmetic exception will result, because the
expression
x / y
Equivalence symbol
Rule 1: !(P  Q) !P || !Q
Rule 2: !(P || Q) !P  !Q
5.3 Boolean Expressions and Variables 233
arithmetic
exception
Table
Table 5.2 The truth table illustrating DeMorgan’s law
P Q !(P  Q) !P | | !Q !(P | | Q) !P  !Q
false false true true true true
false true true true false false
true false true true false false
true true false false false false
wu23399_ch05.qxd 12/14/06 17:49 Page 233
causes a problem known as a divide-by-zero error. Remember that you cannot divide
a number by zero.
However, if we reverse the order to
y == 0 || x / y  z
then no arithmetic exception will occur because the test x / y  z will not be evalu-
ated. For the OR operator ||, if the left operand is evaluated to true, then the right
operand will not be evaluated, because the whole expression is true, whether the
value of the right operand is true or false. We call such an evaluation method a
short-circuit evaluation. For the AND operator , the right operand need not be
evaluated if the left operand is evaluated to false, because the result will then
be false whether the value of the right operand is true or false.
Just as the operator precedence rules are necessary to evaluate arithmetic
expressions unambiguously, they are required for evaluating boolean expressions.
Table 5.3 expands Table 3.3 by including all operators introduced so far.
In mathematics, we specify the range of values for a variable as
80  x  90
In Java, to test that the value for x is within the specified lower and upper bounds,
we express it as
80 = x  x  90
You cannot specify it as
80 = x  90
This is a syntax error because the relational operators (, =, etc.) are binary
operators whose operands must be numerical values. Notice that the result of the
subexpression
80 = x
is a boolean value, which cannot be compared to the numerical value 90. Their data
types are not compatible.
The result of a boolean expression is either true or false, which are the two
values of data type boolean. As is the case with other data types, a value of a data
type can be assigned to a variable of the same data type. In other words, we can
declare a variable of data type boolean and assign a boolean value to it. Here are
examples:
boolean pass, done;
pass = 70  x;
done = true;
One possible use of boolean variables is to keep track of the program settings
or user preferences. A variable (of any data type, not just boolean) used for this
234 Chapter 5 Selection Statements
divide-by-zero
error
short-circuit
evaluation
Wrong
wu23399_ch05.qxd 12/14/06 17:49 Page 234
purpose is called a flag. Suppose we want to allow the user to display either short
or long messages. Many people, when using a new program, prefer to see long
messages, such as Enter a person’s age and press the Enter key to continue. But
once they are familiar with the program, many users prefer to see short messages,
such as Enter age. We can use a boolean flag to remember the user’s preference. We
can set the flag longMessageFormat at the beginning of the program to true or false
depending on the user’s choice. Once this boolean flag is set, we can refer to the
flag at different points in the program as follows:
if (longMessageFormat) {
//display the message in long format
5.3 Boolean Expressions and Variables 235
flag
Table
Table 5.3
Operator precedence rules.Groups are listed in descending order of prece-
dence.An operator with a higher precedence will be evaluated first.If two
operators have the same precedence,then the associativity rule is applied
Group Operator Precedence Associativity
Subexpression ( ) 10 Left to right
(If parentheses are nested,
then innermost subexpres-
sion is evaluated first.)
Postfix ++ 9 Right to left
increment and --
decrement
operators
Unary - 8 Right to left
operators !
Multiplicative * 7 Left to right
operators /
%
Additive + 6 Left to right
operators -
Relational  5 Left to right
operators =

=
Equality == 4 Left to right
operators !=
Boolean AND  3 Left to right
Boolean OR || 2 Left to right
Assignment = 1 Right to left
wu23399_ch05.qxd 12/14/06 17:49 Page 235
} else {
//display the message in short format
}
Notice the value of a boolean variable is true or false, so even though it is
valid, we do not write a boolean expression as
if (isRaining == true) {
System.out.println(Store is open);
} else {
System.out.println(Store is closed);
}
but more succinctly as
if (isRaining) {
System.out.println(Store is open);
} else {
System.out.println(Store is closed);
}
Another point that we have to be careful about in using boolean variables is
the choice of identifier. Instead of using a boolean variable such as motionStatus, it
is more meaningful and descriptive to use the variable isMoving. For example, the
statement
if (isMoving) {
//the mobile robot is moving
} else {
//the mobile robot is not moving
}
is much clearer than the statement
if (motionStatus) {
//the mobile robot is moving
} else {
//the mobile robot is not moving
}
When we define a boolean data member for a class, it is a Java convention to
use the prefix is instead of get for the accessor.
We again conclude the section with a sample class. Let’s improve the
Ch5Account class by adding a boolean data member active to represent the state of
an account. When an account is first open, it is set to an active state. Deposits and
236 Chapter 5 Selection Statements
wu23399_ch05.qxd 12/14/06 17:49 Page 236
withdrawals can be made only when the account is active. If the account is inactive,
then the requested opertion is ignored. Here’s how the class is defined (the actual
class name is Ch5AccountVer2):
5.3 Boolean Expressions and Variables 237
class Ch5AccountVer2 {
// Data Members
private String ownerName;
private double balance;
private boolean active;
//Constructor
public Ch5AccountVer2(String name, double startingBalance ) {
ownerName = name;
balance = startingBalance;
setActive(true);
}
//Adds the passed amount to the balance
public void add(double amt) {
//add if amt is positive; do nothing otherwise
if (isActive()  amt  0) {
balance = balance + amt;
}
}
//Closes the account; set 'active' to false
public void close( ) {
setActive(false);
}
//Deducts the passed amount from the balance
public void deduct(double amt) {
//deduct if amt is positive; do nothing otherwise
if (isActive()  amt  0) {
double newbalance = balance - amt;
if (newbalance = 0) { //don't let the balance become negative
balance = newbalance;
}
}
}
Data Members
add
close
deduct
wu23399_ch05.qxd 12/14/06 17:49 Page 237
//Returns the current balance of this account
public double getCurrentBalance( ) {
return balance;
}
//Returns the name of this account's owner
public String getOwnerName( ) {
return ownerName;
}
//Is the account active?
public boolean isActive( ) {
return active;
}
//Assigns the name of this account's owner
public void setOwnerName(String name) {
ownerName = name;
}
//Sets 'active' to true or false
private void setActive(boolean state) {
active = state;
}
}
238 Chapter 5 Selection Statements
getCurrentBalance
getOwnerName
setOwnerName
isActive
setActive
1. Evaluate the following boolean expressions. Assume x, y, and z have some
numerical values.
a. 4  5 || 6 == 6
b. 2  4  (false || 5 = 4)
c. x = y  !(z != z) || x  y
d. x  y || z  y  y = z
2. Identify errors in the following boolean expressions and assignments. Assume
x and y have some numerical values.
a. boolean done;
done = x = y;
b. 2  4  (3  5) + 1 == 3
c. boolean quit;
quit = true;
quit == ( 34 == 20 )  quit;
wu23399_ch05.qxd 12/14/06 17:49 Page 238
5.4 Comparing Objects
With primitive data types, we have only one way to compare them, but with objects
(reference data type), we have two ways to compare them. We discuss the ways
the objects can be compared in this section. First, let’s review how we compare
primitive data types. What would be the output of the following code?
int num1, num2;
num1 = 15;
num2 = 15;
if (num1 == num2) {
System.out.println(They are equal);
} else {
System.out.println(They are not equal);
}
Because the two variables hold the same value, the output is
They are equal
Now, let’s see how the objects can be compared. We will use String objects for
illustration. Since we use string data all the time in our programs, it is very impor-
tant for us to understand perfectly how String objects can be compared.
Consider the following code that attempts to compare two String objects:
String str1, str2;
str1 = new String(Java);
str2 = new String(Java);
if (str1 == str2) {
System.out.println(They are equal);
5.4 Comparing Objects 239
We introduced the logical AND and OR operations using the symbols  and ||.
In Java, there are single ampersand and single vertical bar operations. For exam-
ple,if we write an if statement as
if ( 70 = x  x  90 )
it will compile and run. Unlike the double ampersand, the single ampersand will
not do a short-circuit evaluation. It will evaluate both left and right operands.The
single vertical bar works in an analogous manner. So, which one should we use?
Use double ampersand for AND and double vertical bars for OR.We will most likely
never encounter a situation where we cannot use the double ampersand or the
double vertical bars.
wu23399_ch05.qxd 12/14/06 17:49 Page 239
} else {
System.out.println(They are not equal);
}
What would be an output? The answer is
They are not equal
The two objects are constructed with the same sequence of characters, but the result
of comparison came back that they were not equal. Why?
When two variables are compared, we are comparing their contents. In the
case of primitive data types, the content is the actual value. In case of reference data
types, the content is the address where the object is stored. Since there are two dis-
tinct String objects, stored at different addresses, the contents of str1 and str2 are
different, and therefore, the equality testing results in false. If we change the code to
String str1, str2;
str1 = new String(Java);
str2 = str1;
if (str1 == str2) {
System.out.println(They are equal);
} else {
System.out.println(They are not equal);
}
then the output would be
They are equal
because now we have one String object and both variables str1 and str2 point to this
object. This means the contents of str1 and str2 are the same because they refer to
the same address. Figure 5.5 shows the distinction.
What can we do if we need to check whether two distinct String objects have
the same sequence of characters? Many standard classes include different types
of comparison methods. The String class, for example, includes the equals and
equalsIgnoreCase comparison methods. The equals method returns true if two String
objects have the exact same sequence of characters. The equalsIgnoreCase method
does the same as the equals method, but the comparison is done in a case-insensitive
manner. Using the equals method, we can rewrite the first sample code as
String str1, str2;
str1 = new String(Java);
str2 = new String(Java);
if (str1.equals(str2)) {
System.out.println(They are equal);
} else {
System.out.println(They are not equal);
}
240 Chapter 5 Selection Statements
No new object is created here.The content
(address) of str1 is copied to str2,making
them both point to the same object.
Use the equals
method.
wu23399_ch05.qxd 12/14/06 17:49 Page 240
and get the result
They are equal
Just as the String and many standard classes provide the equals method, it is
common to include such an equals method in programmer-defined classes also.
Consider a Fraction class. We say two Fraction objects are equal if they have the
same value for the numerator and the denominator. Here’s how we can define the
equals method for the Fraction class:
class Fraction {
private int numerator;
private int denominator;
...
//constructor and other methods
...
public int getNumerator( ) {
return numerator;
}
5.4 Comparing Objects 241
:String
Java
String str1, str2;
str1 = new String(Java);
str2 = new String(Java);
str1
Case A: Two variables refer to two different objects.
:String
Java
:String
Java
str1  str2 false
String str1, str2;
str1 = new String(Java);
str2 = str1;
str1  str2 true
str2
str1
Case B: Two variables refer to the same object.
str2
Figure 5.5 How the equality == testing works with the objects.
wu23399_ch05.qxd 12/14/06 17:49 Page 241
public int getDenominator( ) {
return denominator;
}
public boolean equals(Fraction number) {
return (numerator == number.getNumerator()
 denominator == number.getDenominator());
}
}
Notice that the body of the equals method is a concise version of
if (numerator == number.getNumerator( )
 denominator == number.getDenominator()) {
return true;
} else {
return false;
}
Using the equals method, we can compare two Fraction objects in the follow-
ing manner:
Fraction frac1, frac2;
//create frac1 and frac2 objects
...
if (frac1.equals(frac2)) {
...
}
or equivalently as
if (frac2.equals(frac1)) {
...
}
Note that the equals method as defined is incomplete. For example, if we
compare fractions 48 and 36, using this equals method, we get false as the result
because the method does not compare the fractions in their simplified form. The
method should have reduced both 48 and 36 to 12 and then compared. To
implement a method that reduces a fraction to its simplest form, we need to use a
repetition control statement. We will revisit this problem when we learn how to
write repetition control statements in Chapter 6. Also, we will provide the complete
definition of the Fraction class in Chapter 7.
We conclude this section by presenting an exception to the rule for comparing
objects. This exception applies to the String class only. We already mentioned in
242 Chapter 5 Selection Statements
Compare this object’s values to the
values of number
wu23399_ch05.qxd 12/14/06 17:49 Page 242
Chapter 2 that for the String class only, we do not have to use the new operator to
create an instance. In other words, instead of writing
String str = new String(Java);
we can write
String str = Java;
which is a more common form. These two statements are not identical in terms
of memory allocation, which in turn affects how the string comparisons work.
Figure 5.6 shows the difference in assigning a String object to a variable. If we do
not use the new operator, then string data are treated as if they are a primitive data
type. When we use the same literal String constants in a program, there will be
exactly one String object.
This means we can use the equal symbol == to compare String objects when
no new operators are used. However, regardless of how the String objects are cre-
ated, it is always correct and safe to use the equals and other comparison methods
to compare two strings.
5.4 Comparing Objects 243
:String
Java
String word1, word2;
word1 = new String(Java);
word2 = new String(Java);
word1
:String
Java
:String
Java
word1  word2 false
word2
word1 word2
Whenever the new operator is used,
there will be a new object.
String word1, word2;
word1 = Java;
word2 = Java;
word1  word2 true
Literal string constant such as “Java” will
always refer to one object.
Figure 5.6 Difference between using and not using the new operator for String.
Always use the equals and other comparison methods to compare String objects.
Do not use == even though it may work correctly in certain cases.
wu23399_ch05.qxd 12/14/06 17:49 Page 243
244 Chapter 5 Selection Statements
1. Determine the output of the following code.
String str1 = Java;
String str2 = Java;
boolean result1 = str1 == str2;
boolean result2 = str1.equals(str2);
System.out.println(result1);
System.out.println(result2);
2. Determine the output of the following code.
String str1 = new String(latte);
String str2 = new String(LATTE);
boolean result1 = str1 == str2;
boolean result2 = str1.equals(str2);
System.out.println(result1);
System.out.println(result2);
3. Show the state of memory after the following statements are executed.
String str1, str2, str3;
str1 = Jasmine;
str2 = Oolong;
str3 = str2;
str2 = str1;
5.5 The switch Statement
Another Java statement that implements a selection control flow is the switch
statement. Suppose we want to direct the students to the designated location for
them to register for classes. The location where they register is determined by their
grade level. The user enters 1 for freshman, 2 for sophomore, 3 for junior, and 4 for
senior. Using the switch statement, we can write the code as
int gradeLevel;
Scanner scanner = new Scanner(System.in);
System.out.print(Grade (Frosh-1,Soph-2,...): );
gradeLevel = scanner.nextInt();
switch (gradeLevel) {
case 1: System.out.println(Go to the Gymnasium);
break;
case 2: System.out.println(Go to the Science Auditorium);
break;
switch
statement
wu23399_ch05.qxd 12/14/06 17:49 Page 244
case 3: System.out.println(Go to Halligan Hall Rm 104);
break;
case 4: System.out.println(Go to Root Hall Rm 101);
break;
}
The syntax for the switch statement is
switch ( integer expression ) {
case label 1 : case body 1
...
case label n : case body n
}
Figure 5.7 illustrates the correspondence between the switch statement we wrote
and the general format.
The case label i has the form
case integer constant or default
and case body i is a sequence of zero or more statements. Notice that case body i
is not surrounded by left and right braces. The constant can be either a named or
literal constant.
The data type of arithmetic expression must be char, byte, short, or int.
(Note: We will cover the data type char in Chap. 9.) The value of arithmetic
expression is compared against the constant value i of case label i. If there is a
5.5 The switch Statement 245
switch
statement
syntax
default
reserved word
Figure 5.7 Mapping of the sample switch statement to the general format.
gradeLevel ) {
switch (
}
case 1:
case 2:
System.out.println(Go to the Gymnasium);
break;
System.out.println(Go to the Science Auditorium);
break;
case 3: System.out.println(Go to Halligan Hall Rm 104);
break;
case 4: System.out.println(Go to Root Hall Rm 101);
break;
Integer
Expression
CL1
CB1
CL2
CB2
CL3
CB3
CL4
CB4
CB - case body
CL - case label
wu23399_ch05.qxd 12/14/06 17:49 Page 245
matching case, then its case body is executed. If there is no matching case, then
the execution continues to the statement that follows the switch statement. No two
cases are allowed to have the same value for constant, and the cases can be
listed in any order.
Notice that each case in the sample switch statement is terminated with the
break statement. The break statement causes execution to continue from the state-
ment following this switch statement, skipping the remaining portion of the switch
statement. The following example illustrates how the break statement works:
//Assume necessary declaration and object creation are done
selection = 1;
switch (selection) {
case 0: System.out.println(0);
case 1: System.out.println(1);
case 2: System.out.println(2);
case 3: System.out.println(3);
}
When this code is executed, the output is
1
2
3
because after the statement in case 1 is executed, statements in the remaining cases
will be executed also. To execute statements in one and only one case, we need
to include the break statement at the end of each case, as we have done in the first
example. Figure 5.8 shows the effect of the break statement.
The break statement is not necessary in the last case, but for consistency we
place it in every case. Also, by doing so we don’t have to remember to include the
break statement in the last case when we add more cases to the end of the switch
statement.
Individual cases do not have to include a statement, so we can write some-
thing like this:
Scanner scanner = new Scanner(System.in);
System.out.print(Input: );
int ranking = scanner.nextInt();
switch (ranking) {
case 10:
case 9:
case 8: System.out.println(Master);
break;
case 7:
case 6: System.out.println(Journeyman);
break;
246 Chapter 5 Selection Statements
break
statement
wu23399_ch05.qxd 12/14/06 17:49 Page 246
case 5:
case 4: System.out.println(Apprentice);
break;
}
The code will print Master if the value of ranking is 10, 9, or 8; Journeyman if the
value of ranking is either 7 or 6; or Apprentice if the value of ranking is either 5 or 4.
We may include a default case that will always be executed if there is no
matching case. For example, we can add a default case to print out an error message
if any invalid value for ranking is entered.
switch (ranking) {
case 10:
case 9:
case 8: System.out.println(Master);
break;
case 7:
case 6: System.out.println(Journeyman);
break;
case 5:
5.5 The switch Statement 247
Figure 5.8 A diagram showing the control flow of the switch statement with and without the break statements.
false
false
false
true
true
true
true
true
true
switch ( N ) {
case 1: x = 10; break;
case 2: x = 20; break;
case 3: x = 30; break;
}
switch ( N ) {
case 1: x = 10;
case 2: x = 20;
case 3: x = 30;
}
x = 10;
N == 1?
x = 20;
N == 2?
x = 30;
N == 3?
false
false
false
N == 1?
N == 2?
N == 3?
x = 10;
break;
break;
break;
x = 20;
x = 30;
wu23399_ch05.qxd 12/14/06 17:49 Page 247
case 4: System.out.println(Apprentice);
break;
default: System.out.println(Error: Invalid Data);
break;
}
There can be at most one default case. Since the execution continues to the next
statement if there is no matching case (and no default case is specified), it is safer to
always include a default case. By placing some kind of output statement in the
default case, we can detect an unexpected switch value. Such a style of program-
ming is characterized as defensive programming. Although the default case does not
have to be placed as the last case, we recommend you do so, in order to make the
switch statement more readable.
248 Chapter 5 Selection Statements
defensive
programming
1. What’s wrong with the following switch statement?
switch ( N ) {
case 0:
case 1: x = 11;
break;
default: System.out.println(Switch Error);
break;
case 2: x = 22;
break;
case 1: x = 33;
break;
}
2. What’s wrong with the following switch statement?
switch ( ranking ) {
case 4.55: pay = pay * 0.20;
break;
case =4.55: pay = pay * 0.15;
break;
default: pay = pay * 0.05;
break;
}
5.6 Drawing Graphics
We introduce four standard classes related to drawing geometric shapes on a
window. These four standard classes will be used in Section 5.7 on the sample
development. We describe their core features here. More details can be found in the
online Java API documentation.
wu23399_ch05.qxd 12/14/06 17:49 Page 248
java.awt.Graphics
We can draw geometric shapes on a frame window by calling appropriate methods
of the Graphics object. For example, if g is a Graphics object, then we can write
g.drawRect(50, 50, 100, 30);
to display a rectangle 100 pixels wide and 30 pixels high at the specified position
(50, 50). The position is determined as illustrated in Figure 5.9. The complete pro-
gram is shown below. The top left corner, just below the window title bar, is posi-
tion (0, 0), and the x value increases from left to right and the y value increases from
top to bottom. Notice that the direction in which the y value increases is opposite to
the normal two-dimensional graph.
The area of a frame which we can draw is called the content pane of a frame.
The content pane excludes the area of a frame that excludes the regions such as the
border, scroll bars, the title bar, the menu bar, and others. To draw on the content
pane of a frame window, first we must get the content pane’s Graphic object. Then
we call this Graphics method to draw geometric shapes. Here’s a sample:
5.6 Drawing Graphics 249
java.awt.
Graphics
content pane
of a frame
/*
Chapter 5 Sample Program: Draw a rectangle on a frame
window's content pane
File: Ch5SampleGraphics.java
*/
import javax.swing.*; //for JFrame
import java.awt.*; //for Graphics and Container
class Ch5SampleGraphics {
public static void main( String[] args ) {
JFrame win;
Container contentPane;
Graphics g;
win = new JFrame(My First Rectangle);
win.setSize(300, 200);
win.setLocation(100,100);
win.setVisible(true);
contentPane = win.getContentPane();
g = contentPane.getGraphics();
g.drawRect(50,50,100,30);
}
}
win must be visible on the
screen before you get its
content pane.
wu23399_ch05.qxd 12/14/06 17:49 Page 249
Here are the key points to remember in drawing geometric shapes on the con-
tent pane of a frame window.
250 Chapter 5 Selection Statements
To draw geometric shapes on the content pane of a frame window:
1. The content pane is declared as a Container, for example,
Container contentPane;
2. The frame window must be visible on the screen before we can get its content
pane and the content pane’s Graphics object.
Depending on the speed of your PC, you may have to include the following try
statement
try {Thread.sleep(200);} catch (Exception e) {}
to put a delay before drawing the rectangle. Place this try statement before the
last statement.The argument in the sleep method specifies the amount of delay
in milliseconds (1000 ms = 1 s). If you still do not see a rectangle drawn in the
window after including the delay statement, increase the amount of delay until
you see the rectangle drawn.We will describe the try statement in Chapter 8.
graphic.drawRect( x, y, width, height);
graphic.drawRect(50, 50, 100, 30);
Syntax
A rectangle width
wide and height
high is displayed at
position (x, y).
Position (50, 50)
Position (0, 0)
30
100
x
y
Example:
Figure 5.9 The diagram illustrates how the position of the rectangle is determined by the drawRect method.
wu23399_ch05.qxd 12/14/06 17:49 Page 250
Table 5.4 lists some of the available graphic drawing methods.
5.6 Drawing Graphics 251
Table
Table 5.4 A partial list of drawing methods defined for the Graphics class
Method Meaning
drawLine(x1,y1,x2,y2) Draws a line between (x1,y1) and
(x2,y2).
drawRect(x,y,w,h) Draws a rectangle with width w and height h
at (x,y).
drawRoundRect(x,y,w,h,aw,ah) Draws a rounded-corner rectangle with
width w and height h at (x,y). Parameters
aw and ah determine the angle for the
rounded corners.
(x,y)
h
w
aw
ah
(x,y)
h
w
(x1,y1)
(x2,y2)
If there is a window that covers the area in which the drawing takes place or the draw-
ing window is minimized and restored to its normal size, the drawn shape (or portion of
it, in the case of the overlapping windows) gets erased. The DrawingBoard class used in
the sample development (Sec. 5.7) eliminates this problem. For information on the
technique to avoid the disappearance of the drawn shape, please check our website at
www.drcaffeine.com
wu23399_ch05.qxd 12/14/06 17:49 Page 251
Notice the distinction between the draw and fill methods. The draw method
will draw the boundary only, while the fill method fills the designated area with the
currently selected color. Figure 5.10 illustrates the difference.
java.awt.Color
To designate the color for drawing, we will use the Color class from the standard
java.awt package. A Color object uses a coloring scheme called the RGB scheme,
which specifies a color by combining three values, ranging from 0 to 255, for red,
green, and blue. For example, the color black is expressed by setting red, green,
and blue to 0, and the color white by setting all three values to 255. We create, for
example, a Color object for the pink color by executing
Color pinkColor;
pinkColor = new Color(255,175,175);
252 Chapter 5 Selection Statements
Table
Table 5.4 A partial list of drawing methods defined for the Graphics class (Continued)
Method Meaning
drawOval(x,y,w,h) Draws an oval with width w and height h at
(x,y).
drawString(text,x,y) Draws the string text at (x,y).
fillRect(x,y,w,h) Same as the drawRect method but fills the
region with the currently set color.
fillRoundRect(x,y,w,h,aw,ah) Same as the drawRoundRect method
but fills the region with the currently
set color.
fillOval(x,y,w,h) Same as the drawOval method but fills the
region with the currently set color.
(x,y)
text
(x,y)
h
w
java.awt.Color
wu23399_ch05.qxd 12/14/06 17:49 Page 252
Instead of dealing with the three numerical values, we can use the public class
constants defined in the Color class. The class constants for common colors are
Color.BLACK Color.MAGENTA
Color.BLUE Color.ORANGE
Color.CYAN Color.PINK
Color.DARK_GRAY Color.RED
Color.GRAY Color.WHITE
Color.GREEN Color.YELLOW
Color.LIGHT_GRAY
The class constants in lowercase letters are also defined (such as Color.black,
Color.blue, and so forth). In the older versions of Java, only the constants in
lowercase letters were defined. But the Java convention is to name constants using
only the uppercase letters, so the uppercase color constants are added to the class
definition.
Each of the above is a Color object with its RGB values correctly set up. We
will pass a Color object as an argument to the setColor method of the Graphics class
to change the color. To draw a blue rectangle, for example, we write
//Assume g is set correctly
g.setColor(Color.BLUE);
g.drawRect(50, 50, 100, 30);
We can also change the background color of a content pane by using the
setBackground method of Container as
contentPane.setBackground(Color.LIGHT_GRAY);
5.6 Drawing Graphics 253
g.drawRect(50, 50, 100, 30); g.fillRect(175, 50, 100, 30);
Figure 5.10 The diagram illustrates the distinction between the draw and fill methods.We assume the
currently selected color is black (default).
wu23399_ch05.qxd 12/14/06 17:49 Page 253
Running the following program will result in the frame shown in Figure 5.11.
254 Chapter 5 Selection Statements
Figure 5.11 A frame with a white background content pane and two rectangles.
/*
Chapter 5 Sample Program: Draw one blue rectangle and
one filled red rectangle on light gray
background content pane
File: Ch5SampleGraphics2.java
*/
import javax.swing.*;
import java.awt.*;
class Ch5SampleGraphics2 {
public static void main( String[] args ) {
JFrame win;
Container contentPane;
Graphics g;
win = new JFrame(Rectangles);
win.setSize(300, 200);
win.setLocation(100,100);
win.setVisible(true);
contentPane = win.getContentPane();
contentPane.setBackground(Color.LIGHT_GRAY);
g = contentPane.getGraphics();
g.setColor(Color.BLUE);
g.drawRect(50,50,100,30);
wu23399_ch05.qxd 12/14/06 17:49 Page 254
g.setColor(Color.RED);
g.fillRect(175,50,100,30);
}
}
5.6 Drawing Graphics 255
java.awt.Point
A Point object is used to represent a point in two-dimensional space. It contains x
and y values, and we can access these values via its public data member x and y.
Here’s an example to assign a position (10, 20):
Point pt = new Point();
pt.x = 10;
pt.y = 20;
It is also possible to set the position at the creation time as follows:
Point pt = new Point(10, 20);
java.awt.Dimension
In manipulating shapes, such as moving them around a frame’s content pane, the
concept of the bounding rectangle becomes important. A bounding rectangle is a
rectangle that completely surrounds the shape. Figure 5.12 shows some examples of
bounding rectangles.
Just as the (x, y) values are stored as a single Point object, we can store the width
and height of a bounding rectangle as a single Dimension object. The Dimension class
has the two public data members width and height to maintain the width and height
of a bounding rectangle. Here’s an example to create a 40 pixels by 70 pixels high
bounding rectangle:
Dimension dim = new Dimension();
dim.width = 40;
dim.height = 70;
Bounding rectangle
of a rectangle is the
rectangle itself.
Bounding rectangle
Figure 5.12 Bounding rectangles of various shapes.
wu23399_ch05.qxd 12/14/06 17:49 Page 255
It is also possible to set the values at the creation time as follows:
Dimension dim = new Dimension(40, 70);
Let’s apply the drawing techniques to an early sample program. In Chapter 4,
we wrote the RoomWinner program that randomly selects and displays the dorm
room lottery cards. The display was only in text, something like this:
Winning Card Combination:
1 - red; 2 - green; 3 - blue
color number
Card 1: 2 13
Card 2: 2 12
Card 3: 1 14
We will make a graphical version of the program. Figure 5.13 shows a sample
output.
Here’s the main class Ch5RoomWinner, which has a structure similar to the
one for Ch5SampleGraphics2.
256 Chapter 5 Selection Statements
Figure 5.13 Sample output of Ch5RoomWinner program.
import java.awt.*;
import javax.swing.*;
class Ch5RoomWinner {
public static void main( String[] args ) {
JFrame win;
Container contentPane;
Graphics g;
wu23399_ch05.qxd 12/14/06 17:49 Page 256
GraphicLotteryCard one, two, three;
win = new JFrame(Room Winner);
win.setSize(300, 200);
win.setLocation(100,100);
win.setVisible(true);
contentPane = win.getContentPane();
contentPane.setBackground(Color.WHITE);
g = contentPane.getGraphics();
one = new GraphicLotteryCard( );
two = new GraphicLotteryCard( );
three = new GraphicLotteryCard( );
one.spin();
two.spin();
three.spin();
one.draw(g, 10, 20);
two.draw(g, 50, 20);
three.draw(g, 90, 20);
}
}
5.6 Drawing Graphics 257
These objects will draw themselves
on g at the specified positions.
We modify the LotteryCard class from Chapter 4 by adding code that will draw a
card on a given Graphics context. The name of the new class is GraphicLotteryCard.
Here’s the class definition (we list only the portions that are new):
import java.awt.*;
class GraphicLotteryCard {
// Data Members
//width of this card for drawing
public static final int WIDTH = 30;
//height of this card for drawing
public static final int HEIGHT = 40;
//the other data members and methods are the same as before
public void draw(Graphics g, int xOrigin, int yOrigin) {
switch (color) {
case 1: g.setColor(Color.RED);
break;
wu23399_ch05.qxd 12/14/06 17:49 Page 257
case 2: g.setColor(Color.GREEN);
break;
case 3: g.setColor(Color.BLUE);
break;
}
g.fillRect(xOrigin, yOrigin, WIDTH, HEIGHT);
g.setColor(Color.WHITE); //draw text in white
g.drawString(  + number, xOrigin + WIDTH/4, yOrigin + HEIGHT/2);
}
}
258 Chapter 5 Selection Statements
This is a quick way to convert a
numerical value to String
Notice that the statements in Ch5RoomWinner
one.draw(g, 10, 20);
two.draw(g, 50, 20);
three.draw(g, 90, 20);
are not as flexible as they can be. If the values for the constant WIDTH and HEIGHT
in the GraphicLotteryCard class are changed, these three statements could result in
drawing the card inadequately (such as overlapping cards). The two constants are
declared public for a reason. Using the WIDTH constant, for example, we can rewrite
the three statements as
int cardWidth = GraphicLotteryCard.WIDTH;
one.draw(g, 10, 20);
two.draw(g, 10 + cardWidth + 5, 20);
three.draw(g, 10 + 2*(cardWidth+ 5), 20);
The statements will draw cards with a 5-pixel interval between cards. This code will
continue to work correctly even after the value of WIDTH is modified.
5.7 Enumerated Constants
We learned in Section 3.3 how to define numerical constants and the benefits of
using them in writing readable programs. In this section, we will introduce an
additional type of constant called enumerated constants that were added to the Java
language from Version 5.0. Let’s start with an example. Suppose we want to define
a Student class and define constants to distinguish four undergraduate grade
enumerated
constants
wu23399_ch05.qxd 12/14/06 17:49 Page 258
levels—freshman, sophomore, junior, and senior. Using the numerical constants,
we can define the grade levels as such:
class Student {
public static final int FRESHMAN = 0;
public static final int SOPHOMORE = 1;
public static final int JUNIOR = 2;
public static final int SENIOR = 3;
...
}
With the new enumerated constants, this is how we can define the grade lev-
els in the Student class:
class Student {
public static enum GradeLevel
{FRESHMAN, SOPHOMORE, JUNIOR, SENIOR}
...
}
The word enum is a new reserved word, and the basic syntax for defining enumer-
ated constants is
enum enumerated type { constant values }
where enumerated type is an identifier and constant values is a list of identi-
fiers separated by commas. Notice that for the most common usage of enumerated
constants, we append the modifiers public and static; but they are not a required part
of defining enumerated constants. Here are more examples:
enum Month {JANUARY, FEBRUARY, MARCH, APRIL,
MAY, JUNE, JULY, AUGUST,
SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER}
enum Gender {MALE, FEMALE}
enum SkillLevel {NOVICE, INTERMEDIATE, ADVANCED, EXPERT}
One restriction when declaring an enumerated type is that it cannot be a local
declaration. In other words, we must declare it outside of any method, just as for the
other data members of a class.
Unlike numerical constants, which are simply identifiers with fixed numerical
values, enumerated constants do not have any assigned numerical values. They are
said to belong to, or be members of, the associated enumerated type. For example,
two enumerated constants MALE and FEMALE belong to the enumerated type
Gender. (Note: We keep the discussion of the enumerated type to its simplest form
here. It is beyond the scope of an introductory programming textbook to discuss
Java’s enumerated type in full detail.)
5.7 Enumerated Constants 259
enumerated
type
wu23399_ch05.qxd 12/14/06 17:49 Page 259
Just as with any other data types, we can declare variables of an enumerated
type and assign values to them. Here is an example (for the sake of brevity, we list
the enum declaration and its usage together, but remember that the declaration in
the actual use cannot be a local declaration):
enum Fruit {APPLE, ORANGE, BANANA}
Fruit f1, f2, f3;
f1 = Fruit.APPLE;
f2 = Fruit.BANANA;
f3 = f1;
Because variables f1, f2, and f3 are declared to be of the type Fruit, we can only
assign one of the associated enumerated constants to them. This restriction supports
a desired feature called type safety. So what is the big deal? Consider the following
numerical constants and assignment statements:
final int APPLE = 1;
final int ORANGE = 2;
final int BANANA = 3;
int fOne, fTwo, fThree;
fOne = APPLE;
fTwo = ORANGE;
fThree = fOne;
The code may look comparable to the one that uses enumerated constants, but
what will happen if we write the following?
fOne = 45;
The assignment is logically wrong. It does not make any sense to assign meaning-
less value such as 45 to the variable fOne if it is supposed to represent one of the de-
fined fruit. However, no compiler error will result because the data type of fOne is
int. The statement may or may not cause the runtime error depending on how the
variable fOne is used in the rest of the program. In either case, the program cannot
be expected to produce a correct result because of the logical error.
By defining an enumerated type, a variable of that type can only accept the
associated enumerated constants. Any violation will be detected by the compiler.
This will eliminate the possibility of assigning a nonsensical value as seen in
the case for the numerical constants. Type safety means that we can assign only
meaningful values to a declared variable.
Another benefit of the enumerated type is the informative output values. As-
suming the variables fTwo and f2 retain the values assigned to them in the sample
code, the statement
System.out.println(Favorite fruit is  + fTwo);
260 Chapter 5 Selection Statements
NOTE: The constant value is prefixed
by the name of the enumerated type.
type safety
wu23399_ch05.qxd 12/14/06 17:49 Page 260
will produce a cryptic output:
Favorite fruit is 2
In contrast, the statement
System.out.println(Favorite fruit is  + f2);
will produce a more informative output:
Favorite fruit is BANANA
We will describe other advantages of using enumerated types later in the book.
As shown, when referring to an enumerated constant in the code, we must pre-
fix it with its enumerated type name, for example,
Fruit f = Fruit.APPLE;
. . .
if (f == Fruit.ORANGE) {
System.out.println(I like orange, too.);
}
. . .
A case label for a switch statement is the only exception to this rule. Instead of
writing, for example,
Fruit fruit;
fruit = ... ;
switch (fruit) {
case Fruit.APPLE: . . .;
break;
case Fruit.ORANGE: . . .;
break;
case Fruit.BANANA: . . .;
break;
}
we can specify the case labels without the prefix as in
Fruit fruit;
fruit = ...;
switch (fruit) {
case APPLE: . . .;
break;
5.7 Enumerated Constants 261
wu23399_ch05.qxd 12/14/06 17:49 Page 261
case ORANGE: . . .;
break;
case BANANA: . . .;
break;
}
262 Chapter 5 Selection Statements
It is not necessary to prefix the enumerated constant with its enumerated type
name when it is used as a case label in a switch statement.
The enumerated type supports a useful method named valueOf. The method
accepts one String argument and returns the enumerated constant whose value
matches the given argument. For example, the following statement assigns the enu-
merated constant APPLE to the variable fruit:
Fruit fruit = Fruit.valueOf(APPLE);
In which situations could the valueOf method be useful? One is the input rou-
tine. Consider the following:
Scanner scanner = new Scanner(System.in);
System.out.print(Enter your favorite fruit  +
(APPLE, ORANGE, BANANA): );
String fruitName = scanner.next( );
Fruit favoriteFruit = Fruit.valueOf(fruitName);
Be aware, however, that if you pass a String value that does not match any of the de-
fined constants, it will result in a runtime error. This means if the user enters
Orange, for example, it will result in an error (the input has to be all capital letters
to match). We will discuss how to handle such runtime errors without causing the
program to terminate abruptly in Chapter 8.
To access the enumerated constants in a programmer-defined class from outside
the class, we must reference them through the associated enumerated type (assuming,
of course, the visibility modifier is public). Consider the following Faculty class:
class Faculty {
public static enum Rank
{LECTURER, ASSISTANT, ASSOCIATE, FULL}
private Rank rank;
. . .
wu23399_ch05.qxd 12/14/06 17:49 Page 262
public void setRank(Rank r) {
rank = r;
}
public Rank getRank( ) {
return rank;
}
. . .
}
Notice how the enumerate type Rank is used in the setRank and getRank methods.
It is treated just as any other types are. To access the Rank constants from outside of
the Faculty class, we write
Faculty.Rank.ASSISTANT
Faculty.Rank.FULL
and so forth. Here’s an example that assigns the rank of ASSISTANT to a Faculty
object:
Faculty prof = new Faculty(...);
prof.setRank(Faculty.Rank.ASSISTANT);
And here’s an example to retrieve the rank of a Faculty object:
Faculty teacher;
//assume 'teacher' is properly created
Faculty.Rank rank;
rank = teacher.getRank();
5.7 Enumerated Constants 263
1. Define an enumerated type Day that includes the constants SUNDAY through
SATURDAY.
2. What is the method that returns an enumerated constant, given the matching
String value?
3. Detect the error(s) in the following code:
enum Fruit {APPLE, ORANGE, BANANA}
Fruit f1, f2;
int f3;
f1 = 1;
f2 = ORANGE;
f3 = f1;
f1 = BANANA;
wu23399_ch05.qxd 12/14/06 17:49 Page 263
Sample Development
Drawing Shapes
Whenacertainperiodoftimepasseswithoutanyactivityonacomputer,ascreensaverbe-
comes active and draws different types of geometric patterns or textual messages.
In this section we will develop an application that simulates a screensaver.We will learn a
development skill very commonly used in object-oriented programming. Whether we
develop alone or as a project team member, we often find ourselves writing a class that
needs to behave in a specific way so that it works correctly with other classes.The other
classes may come from standard Java packages or could be developed by the other team
members.
In this particular case, we use the DrawingBoard class (written by the author). This
is a helper class that takes care of programming aspects we have not yet mastered,such as
moving multiple geometric shapes in a smooth motion across the screen.It is not an issue
of whether we can develop this class by ourselves, because no matter how good we be-
come as programmers,we would rarely develop an application completely on our own.
We already used many predefined classes from the Java standard libraries, but
the way we will use the predefined class here is different.When we developed programs
before, the classes we wrote called the methods of predefined classes.Our main method
creating a GregorianCalendar object and calling its methods is one example.Here,for us
to use a predefined class, we must define another class that provides necessary services
to this predefined class. Figure 5.14 differentiates the two types of predefined classes.
The first type does not place any restriction other than calling the methods correctly,
while the second type requires us to implement helper classes in a specific manner to
support it.
In our case, the predefined class DrawingBoard will require another class named
DrawableShape that will assume the responsibility of drawing individual geometric
shapes.So,to use the DrawingBoard class in our program,we must implement the class
named DrawableShape. And we must implement the DrawableShape class in a specific
way.The use of the DrawingBoard class dictates that we define a set of fixed methods in
the DrawableShape class. We can add more, but we must at the minimum provide the
specified set of fixed methods because the DrawingBoard class will need to call these
methods.The methods are“fixed”in the method signature—method name,the number of
parameters and their types,and return type—but the method body can be defined in any
way we like.This is how the flexibility is achieved.For example,the DrawableShape class we
define must include a method named draw with the dictated signature.But it’s up to us to
decide what we put in the method body.So we can choose,for example,to implement the
method to draw a circle,rectangle,or any other geometric shape of our choosing.
As always,we will develop this program following incremental development steps.
The incremental development steps we will take here are slightly different in character
from those we have seen so far. In the previous incremental developments, we knew all
the ingredients,so to speak.Here we have to include a step to explore the DrawingBoard
class.We will find out shortly that to use the DrawingBoard class,we will have to deal with
some Java standard classes we have not seen yet. Pedagogically, a textbook may try to
264 Chapter 5 Selection Statements
5.8 Sample Development
wu23399_ch05.qxd 12/14/06 17:50 Page 264
5.8 Sample Development 265
Figure 5.14 Two types of predefined classes.The first type does not require us to do anything more
than use the predefined classes by calling their methods.The second type requires us to define helper
classes for the predefined classes we want to use.
MyClass2
To use type 2 predefined
classes, we must define
helper classes required by
the predefined classes.
There's no restriction in
using type 1 predefined
classes other than calling
their methods correctly.
MyClass1 Type1Class
Type2Class MyHelperClass
A class we implement
A class given to us
explain beforehand everything that is necessary to understand the sample programs.
But no textbook can explain everything.When we develop programs,there will always be
a time when we encounter some unknown classes. We need to learn how to deal with
such a situation in our development steps.
Problem Statement
Write an application that simulates a screensaver by drawing various geometric
shapes in different colors.The user has the option of choosing a type (ellipse or
rectangle),color,and movement (stationary,smooth,or random).
Overall Plan
We will begin with our overall plan for the development. Let’s begin with the outline of
program logic.We first let the user select the shape, its movement, and its color, and we
then start drawing.We express the program flow as having four tasks:
1. Get the shape the user wants to draw.
2. Get the color of the chosen shape.
3. Get the type of movement the user wants to use.
4. Start the drawing.
program
tasks
wu23399_ch05.qxd 12/14/06 17:50 Page 265
5.8 Sample Development—continued
266 Chapter 5 Selection Statements
Let’s look at each task and determine an object that will be responsible for handling
the task. For the first three tasks, we can use our old friend Scanner. We will get into the
details of exactly how we ask the users to input those values in the later incremental
steps.For the last task of actually drawing the selected shape,we need to define our own
class.The task is too specific to the program, and there is no suitable object in the stan-
dard packages that does the job.As discussed earlier,we will use a given predefined class
DrawingBoard and define the required helper class DrawableShape.
We will define a top-level control object that manages all these objects.We will call
this class Ch5DrawShape. As explained in Section 4.10, we will make this control object
the main class.Here’s our working design document:
Design Document: Ch5DrawShape
Class Purpose
Ch5DrawShape The top-level control object that manages other objects
in the program.This is the main class,as explained in
Section 4.10.
DrawingBoard The given predefined class that handles the movement of
DrawableShape objects.
DrawableShape The class for handling the drawing of individual shapes.
Scanner The standard class for handling input routines.
program
classes
Ch5DrawShape Scanner
DrawingBoard DrawableShape
Figure 5.15 The program diagram for the Ch5DrawShape program.
Figure 5.15 is the program diagram for this program.
wu23399_ch05.qxd 12/14/06 17:50 Page 266
5.8 Sample Development 267
We will implement this program in the following six major steps:
1. Start with a program skeleton.Explore the DrawingBoard class.
2. Define an experimental DrawableShape class that draws a dummy shape.
3. Add code to allow the user to select a shape.Extend the DrawableShape and
other classes as necessary.
4. Add code to allow the user to specify the color.Extend the DrawableShape and
other classes as necessary.
5. Add code to allow the user to specify the motion type.Extend the DrawableShape
and other classes as necessary.
6. Finalize the code by tying up loose ends.
Our first task is to find out about the given class.We could have designed the input rou-
tines first,but without knowing much about the given class,it would be difficult to design
suitable input routines.When we use an unknown class,it is most appropriate to find out
more about this given class before we plan any input or output routines. Just as the de-
velopment steps are incremental, our exploration of the given class will be incremental.
Instead of trying to find out everything about the class at once, we begin with the basic
features and skeleton code. As we learn more about the given class incrementally, we
extend our code correspondingly.
Step 1 Development: Program Skeleton
We begin the development with the skeleton main class.The main purpose in step 1 is to
use the DrawingBoard class in the simplest manner to establish the launch pad for the
development. To do so, we must first learn a bit about the given DrawingBoard class.
Here’s a brief description of the DrawingBoard class. In a real-world situation, we would
be finding out about the given class by reading its accompanying documentation or
some other external sources. The documentation may come in form of online javadoc
documents or reference manuals.
development
steps
step 1
design
DrawingBoard
An instance of this class will support the drawing of DrawableShape objects.Shapes
can be drawn at fixed stationary positions,at random positions,or in a smooth motion
at the specified speed.The actual drawing of the individual shapes is done inside the
DrawableShape class.The client programmer decides which shape to draw.
public void addShape ( DrawableShape shape )
Adds shape to this DrawingBoard object.You can add an unlimited number
of DrawableShape objects.
(Continued)
wu23399_ch05.qxd 12/14/06 17:50 Page 267
5.8 Sample Development—continued
268 Chapter 5 Selection Statements
DrawingBoard (Continued)
public void setBackground( java.awt.Color color )
Sets the background color of this DrawingBoard object to the designated
color.The default background color is black.
public void setDelayTime( double delay )
Sets the delay time between drawings to delay seconds.The smaller the delay
time,the faster the shapes move.If the movement type is other than SMOOTH,
then setting the delay time has no visual effect.
public void setMovement( Movement type )
Sets the movement type to type.Class constants for three types of motion are
Movement.STATIONARY—draw shapes at fixed positions,
Movement.RANDOM—draw shapes at random positions,and
Movement.SMOOTH—draw shapes in a smooth motion.
public void setVisible( boolean state )
Makes this DrawingBoard object appear on or disappear from the screen
if state is true or false, respectively. To simulate the screensaver,
setting it visible will cause a maximized window to appear on the screen.
public void start( )
Starts the drawing.If the window is not visible yet,it will be made visible before
the drawing begins.
Among the defined methods, we see the setVisible method is the one to make
it appear on the screen. All other methods pertain to adding DrawableShape objects
and setting the properties of a DrawingBoard object. We will explain the standard
java.awt.Color class when we use the setBackground method in the later step. In this
step, we will keep the code very simple by only making it appear on the screen. We will
deal with other methods in the later steps.
Our working design document for the Ch5DrawShape class is as follows:
Design Document:The Ch5DrawShape Class
Method Visibility Purpose
constructor public Creates a DrawingBoard object.
main public This is main method of the class.
start public Starts the program by opening a
DrawingBoard object.
wu23399_ch05.qxd 1/25/07 13:50 Page 268
Since this is a skeleton code,it is very basic.Here’s the code:
5.8 Sample Development 269
step 1 code
The purpose of step 1 testing is to verify that a DrawingBoard object appears
correctly on the screen. Since this is our first encounter with the DrawingBoard class,
it is probable that we are not understanding its documentation fully and completely.
We need to verify this in this step. When a maximized window with the black back-
ground appears on the screen, we know the main class was executed properly.
After we verify the correct execution of the step 1 program, we will proceed to imple-
ment additional methods of Ch5DrawShape and gradually build up the required
DrawableShape class.
Step 2 Development: Draw a Shape
In the second development step, we will implement a preliminary DrawableShape
class and make some shapes appear on a DrawingBoard window. To draw shapes,
we need to add them to a DrawingBoard window. And to do so, we need to define the
step 1 test
step 2
design
/*
Chapter 5 Sample Development: Drawing Shapes (Step 1)
The main class of the program.
*/
class Ch5DrawShape {
private DrawingBoard canvas;
public Ch5DrawShape( ) {
canvas = new DrawingBoard( );
}
public void start( ) {
canvas.setVisible(true);
}
public static void main(String[] args) {
Ch5DrawShape screensaver = new Ch5DrawShape( );
screensaver.start();
}
}
wu23399_ch05.qxd 12/14/06 17:50 Page 269
5.8 Sample Development—continued
270 Chapter 5 Selection Statements
DrawableShape class with the specified set of methods. Here are the required methods
and a brief description of what to accomplish in them:
Required Methods of DrawableShape
public void draw(java.awt.Graphics)
Draw a geometric shape on the java.awt.Graphics. The DrawingBoard
window calls the draw method of DrawableShape objects added to it.
public java.awt.Point getCenterPoint( )
Return the center point of this shape.
public java.awt.Dimension getDimension( )
Return the bounding rectangle of this shape as a Dimension.
public void setCenterPoint( java.awt.Point )
Set the center point of this shape.The DrawingBoard window calls the
setCenterPoint method of DrawableShape objects to update their
positions in the SMOOTH movement type.
At this stage, the main task is for us to confirm our understanding of the require-
ments in implementing the DrawableShape class.Once we get this confirmation,we can
get into the details of the full-blown DrawableShape class.
To keep the preliminary class simple, we draw three filled circles of a fixed size and
color. The DrawableShape class includes a single data member centerPoint to keep
track of the shape’s center point.If we fix the radius of the circles to 100 pixels,that is,the
bounding rectangle is 200 pixels by 200 pixels, and the color to blue, then the draw
method can be written as follows:
public void draw(Graphics g) {
g.setColor(Color.blue);
g.fillOval(centerPoint.x-100, centerPoint.y-100, 200, 200);
}
Since the size is fixed, we simply return a new Dimension object for the
getDimension method:
public Dimension getDimension( ) {
return new Dimension(200, 200);
}
For the setCenterPoint and getCenterPoint methods, we assign the passed para-
meter to the data member centerPoint and return the current value of the data member
centerPoint, respectively.
200
200
100
wu23399_ch05.qxd 12/14/06 17:50 Page 270
We are now ready to modify the Ch5DrawShape class to draw three filled circles.
We will implement this by modifying the start method. First we need to create three
DrawableShape objects and add them to the DrawingBoard object canvas:
DrawableShape shape1 = new DrawableShape();
DrawableShape shape2 = new DrawableShape();
DrawableShape shape3 = new DrawableShape();
shape1.setCenterPoint(new Point(250,300));
shape2.setCenterPoint(new Point(500,300));
shape3.setCenterPoint(new Point(750,300));
canvas.addShape(shape1);
canvas.addShape(shape2);
canvas.addShape(shape3);
Then we set the motion type to SMOOTH movement, make the window appear on the
screen,and start the drawing:
canvas.setMovement(DrawingBoard.Movement.SMOOTH);
canvas.setVisible(true);
canvas.start();
Here’s the code for the preliminary DrawableShape class:
5.8 Sample Development 271
step 2 code
import java.awt.*;
/*
Step 2: Add a preliminary DrawableShape class
A class whose instances know how to draw themselves.
*/
class DrawableShape {
private Point centerPoint;
public DrawableShape( ) {
centerPoint = null;
}
public void draw(Graphics g) {
g.setColor(Color.blue);
g.fillOval(centerPoint.x-100, centerPoint.y-100, 200, 200);
}
Data Members
Constructors
draw
wu23399_ch05.qxd 12/14/06 17:50 Page 271
5.8 Sample Development—continued
The Ch5DrawShape class now has the modified start method as designed (the rest of
the class remains the same):
272 Chapter 5 Selection Statements
public Point getCenterPoint( ) {
return centerPoint;
}
public Dimension getDimension( ) {
return new Dimension(200, 200);
}
public void setCenterPoint(Point point) {
centerPoint = point;
}
}
getCenterPoint
getDimension
setCenterPoint
import java.awt.*;
/*
Chapter 5 Sample Development: Start drawing shapes (Step 2)
The main class of the program.
*/
class Ch5DrawShape {
. . .
public void start( ) {
DrawableShape shape1 = new DrawableShape();
DrawableShape shape2 = new DrawableShape();
DrawableShape shape3 = new DrawableShape();
shape1.setCenterPoint(new Point(250,300));
shape2.setCenterPoint(new Point(500,300));
shape3.setCenterPoint(new Point(750,300));
canvas.addShape(shape1);
canvas.addShape(shape2);
canvas.addShape(shape3);
start
wu23399_ch05.qxd 12/14/06 17:50 Page 272
canvas.setMovement(DrawingBoard.Movement.SMOOTH);
canvas.setVisible(true);
canvas.start();
}
. . .
}
5.8 Sample Development 273
Now we run the program and verify the three bouncing circles moving around.To
test other options of the DrawingBoard class,we will try the other methods with different
parameters:
Method Test Parameter
setMovement Try both DrawingBoard.STATIONARY and
DrawingBoard.RANDOM.
setDelayTime Try values ranging from 0.1 to 3.0.
setBackground Try several different Color constants such
as Color.white, Color.red,and
Color.green.
We insert these testing statements before the statement
canvas.setVisible(true);
in the start method.
Another testing option we should try is the drawing of different geometric shapes.
We can replace the drawing statement inside the draw method from
g.fillOval(centerPoint.x-100, centerPoint.y-100,
200, 200);
to
g.fillRect(centerPoint.x-100, centerPoint.y-100,
200, 200);
or
g.fillRoundRect(centerPoint.x-100, centerPoint.y-100,
200, 200, 50, 50);
to draw a filled rectangle or a filled rounded rectangle,respectively.
step 2 test
wu23399_ch05.qxd 12/14/06 17:50 Page 273
5.8 Sample Development—continued
Step 3 Development: Allow the User to Select a Shape
Now that we know how to interact with the DrawingBoard class, we can proceed to
develop the user interface portion of the program. There are three categories in which
the user can select an option: shape, color, and motion.We will work on the shape selec-
tion here and on the color and motion selection in the next two steps.Once we are done
with this step,the next two steps are fairly straightforward because the idea is essentially
the same.
Let’s allow the user to select one of three shapes—ellipse, rectangle, and rounded
rectangle—the shapes we know how to draw at this point.We can add more fancy shapes
later. In what ways should we allow the user to input the shape? There are two possible
alternatives:The first would ask the user to enter the text and spell out the shape,and the
second would ask the user to enter a number that corresponds to the shape (1 for ellipse,
2 for rectangle,3 for rounded rectangle,e.g.).Which is the better alternative?
We anticipate at least two problems with the first input style.When we need to get
a user’s name,for example,there’s no good alternative other than asking the user to enter
his or her name.But when we want the user to select one of the few available choices,it is
cumbersome and too much of a burden for the user. Moreover, it is prone to mistyping.
To allow the user to make a selection quickly and easily, we can let the user select
one of the available choices by entering a corresponding number.We will list the choices
with numbers 1,2,and 3 and get the user’s selection as follows:
System.out.print(Selection: Enter the Shape numbern +
 1 - Ellipse n +
 2 - Rectangle n +
 3 - Rounded Rectangle n);
int selection = scanner.nextInt();
For getting the dimension of the shape, we accept the width and height values
from the user. The values cannot be negative, for sure, but we also want to restrict the
values to a certain range.We do not want the shape to be too small or too large. Let’s set
the minimum to 100 pixels and the maximum to 500 pixels. If the user enters a value
outside the acceptable range,we will set the value to 100.The input routine for the width
can be written as follows:
System.out.print(Enter the width of the shapen +
between 100 and 500 inclusive: );
int width = scanner.nextInt();
if (width  100 || width  500) {
width = 100;
}
The input routine for the height will work in the same manner.
274 Chapter 5 Selection Statements
step 3
design
design
alternative 1
design
alternative 2
wu23399_ch05.qxd 12/14/06 17:50 Page 274
For getting the x and y values of the shape’s center point, we follow the pattern of
getting the width and height values. We will set the acceptable range for the x value to
200 and 800,inclusive,and the y value to 100 and 600,inclusive.
Our next task is to modify the DrawableShape class so it will be able to draw three
different geometric shapes. First we change the constructor to accept the three input
values:
public DrawableShape(Type sType, Dimension sDim,
Point sCenter) {
type = sType;
dimension = sDim;
centerPoint = sCenter;
}
The variables type, dimension, and centerPoint are data members for keeping track of
necessary information.
Next,we define the data member constants as follows:
public static enum Type {ELLIPSE, RECTANGLE, ROUNDED_RECTANGLE}
private static final Dimension DEFAULT_DIMENSION
= new Dimension(200, 200);
private static final Point DEFAULT_CENTER_PT
= new Point(350, 350);
In the previous step,the draw method drew a fixed-size circle.We need to modify it
to draw three different geometric shapes based on the value of the data member type.
We can modify the method to
public void draw(Graphics g) {
g.setColor(Color.blue);
drawShape(g);
}
with the private method drawShape defined as
private void drawShape(Graphics g) {
switch (type) {
case ELLIPSE:
//code to draw a filled oval comes here
break;
case RECTANGLE:
//code to draw a filled rectangle comes here
break;
5.8 Sample Development 275
wu23399_ch05.qxd 12/14/06 17:50 Page 275
5.8 Sample Development—continued
case ROUNDED_RECTANGLE:
//code to draw a filled rounded rectangle
//comes here
break;
}
}
Here’s the modified main class Ch5DrawShape:
276 Chapter 5 Selection Statements
step 3 code
import java.awt.*;
import java.util.*;
/*
Chapter 5 Sample Development: Handle User Input for Shape Type (Step 3)
The main class of the program.
*/
class Ch5DrawShape {
. . .
public void start( ) {
DrawableShape shape1 = getShape();
canvas.addShape(shape1);
canvas.setMovement(DrawingBoard.SMOOTH);
canvas.setVisible(true);
canvas.start();
}
private DrawableShape getShape( ) {
DrawableShape.Type type = inputShapeType();
Dimension dim = inputDimension();
Point centerPt = inputCenterPoint();
DrawableShape shape = new DrawableShape(type, dim, centerPt);
return shape;
}
start
getShape
wu23399_ch05.qxd 1/12/07 10:45 Page 276
private DrawableShape.Type inputShapeType( ) {
System.out.print(Selection: Enter the Shape numbern +
 1 - Ellipse n +
 2 - Rectangle n +
 3 - Rounded Rectangle n);
int selection = scanner.nextInt();
DrawableShape.Type type;
switch (selection) {
case 1: type = DrawableShape.Type.ELLIPSE;
break;
case 2: type = DrawableShape.Type.RECTANGLE;
break;
case 3: type = DrawableShape.Type.ROUNDED_RECTANGLE;
break;
default: type = DrawableShape.Type.ELLIPSE;
break;
}
return type;
}
private Dimension inputDimension( ) {
System.out.print(Enter the width of the shapen +
between 100 and 500 inclusive: );
int width = scanner.nextInt();
if (width  100 || width  500) {
width = 100;
}
System.out.print(Enter the height of the shapen +
between 100 and 500 inclusive: );
int height = scanner.nextInt();
if (height  100 || height  500) {
height = 100;
}
return new Dimension(width, height);
}
5.8 Sample Development 277
inputShapeType
inputDimension
wu23399_ch05.qxd 12/14/06 17:51 Page 277
5.8 Sample Development—continued
private Point inputCenterPoint( ) {
System.out.print(Enter the x value of the center pointn +
between 200 and 800 inclusive: );
int x = scanner.nextInt();
if (x  200 || x  800) {
x = 200;
}
System.out.print(Enter the y value of the center pointn +
between 100 and 500 inclusive: );
int y = scanner.nextInt();
if (y  100 || y  500) {
y = 100;
}
return new Point(x, y);
}
. . .
}
278 Chapter 5 Selection Statements
inputCenterPoint
Data Members
The DrawableShape class is now modified to this:
import java.awt.*;
/*
Step 3: Draw different shapes
A class whose instances know how to draw themselves.
*/
class DrawableShape {
public static enum Type {ELLIPSE, RECTANGLE, ROUNDED_RECTANGLE}
private static final Dimension DEFAULT_DIMENSION
= new Dimension(200, 200);
wu23399_ch05.qxd 12/14/06 17:51 Page 278
private static final Point DEFAULT_CENTER_PT = new Point(350, 350);
private Point centerPoint;
private Dimension dimension;
private Type type;
public DrawableShape(Type sType, Dimension sDim, Point sCenter) {
type = sType;
dimension = sDim;
centerPoint = sCenter;
}
public void draw(Graphics g) {
g.setColor(Color.blue);
drawShape(g);
}
. . .
public void setType(Type shapeType) {
type = shapeType;
}
private void drawShape(Graphics g) {
switch (type) {
case ELLIPSE:
g.fillOval(centerPoint.x - dimension.width/2,
centerPoint.y - dimension.height/2,
dimension.width,
dimension.height);
break;
case RECTANGLE:
g.fillRect(centerPoint.x - dimension.width/2,
centerPoint.y - dimension.height/2,
dimension.width,
dimension.height);
break;
case ROUNDED_RECTANGLE:
g.fillRoundRect(centerPoint.x - dimension.width/2,
centerPoint.y - dimension.height/2,
dimension.width,
dimension.height,
5.8 Sample Development 279
Constructor
draw
setType
drawShape
wu23399_ch05.qxd 12/14/06 17:51 Page 279
5.8 Sample Development—continued
(int) (dimension.width * 0.3),
(int) (dimension.height * 0.3));
break;
}
}
}
280 Chapter 5 Selection Statements
Notice how we add code for handling the case when an invalid number is entered in
the inputShapeType method.We use the default case to set the shape type to ELLIPSE
if an invalid value is entered. In addition to handling the invalid entries, it is critical for us
to make sure that all valid entries are handled correctly.For example,we cannot leave the
type undefined or assigned to a wrong value when one of the valid data is entered.
When we write a selection control statement,we must make sure that all possible
cases are handled correctly.
Now we run the program multiple times, trying various shape types, dimensions,
and center points. After we verify that everything is working as expected, we proceed to
the next step.
Step 4 Development: Allow the User to Select a Color
In the fourth development step,we add a routine that allows the user to specify the color
of the selected shape.We adopt the same input style for accepting the shape type as in
step 3.We list five different color choices and let the user select one of them by entering
the corresponding number. We use a default color when an invalid number is entered.
Analogous to the shape selection routine,we will add a method named inputColor to the
Ch5DrawShape class.The structure of this method is identical to that of the input meth-
ods, except the return type is Color. Using the inputColor method, we can define the
getShape method as follows:
private DrawableShape getShape( ) {
DrawableShape.Type type = inputShapeType();
Dimension dim = inputDimension();
step 3 test
step 4
design
wu23399_ch05.qxd 12/14/06 17:51 Page 280
Point centerPt = inputCenterPoint();
Color color = inputColor();
DrawableShape shape
= new DrawableShape(type, dim, centerPt, color);
return shape;
}
We make a small extension to the DrawableShape class by changing the con-
structor to accept a color as its fourth argument and adding a data member to keep track
of the selected color.
Here’s the modified Ch5DrawShape class:
5.8 Sample Development 281
step 4 code
import java.awt.*;
import java.util.*;
/*
Chapter 5 Sample Development: Color selection (Step 4)
The main class of the program.
*/
class Ch5DrawShape {
. . .
private DrawableShape getShape( ) {
DrawableShape.Type type = inputShapeType();
Dimension dim = inputDimension();
Point centerPt = inputCenterPoint();
Color color = inputColor();
DrawableShape shape
= new DrawableShape(type, dim, centerPt, color);
return shape;
}
private Color inputColor( ) {
System.out.print(Selection: Enter the Color numbern +
 1 - Red n +
 2 - Green n +
 3 - Blue n +
 4 - Yellow n +
 5 - Magenta n);
int selection = scanner.nextInt();
getShape
inputColor
wu23399_ch05.qxd 12/14/06 17:51 Page 281
5.8 Sample Development—continued
Color color;
switch (selection) {
case 1: color = Color.red;
break;
case 2: color = Color.green;
break;
case 3: color = Color.blue;
break;
case 4: color = Color.yellow;
break;
case 5: color = Color.magenta;
break;
default: color = Color.red;
break;
}
return color;
}
. . .
}
282 Chapter 5 Selection Statements
The DrawableShape class is now modified to this:
import java.awt.*;
/*
Step 4: Adds the color choice
A class whose instances know how to draw themselves.
*/
class DrawableShape {
. . .
private static final Color DEFAULT_COLOR = Color.BLUE;
. . . Data Members
wu23399_ch05.qxd 12/14/06 17:51 Page 282
private Color fillColor;
. . .
public DrawableShape(Type sType, Dimension sDim,
Point sCenter, Color sColor) {
type = sType;
dimension = sDim;
centerPoint = sCenter;
fillColor = sColor;
}
public void draw(Graphics g) {
g.setColor(fillColor);
drawShape(g);
}
. . .
}
5.8 Sample Development 283
Constructor
draw
Now we run the program several times,each time selecting a different color,and we
verify that the shape is drawn in the chosen color. After we verify the program, we move
on to the next step.
Step 5 Development: Allow the User to Select a Motion Type
In the fifth development step, we add a routine that allows the user to select the motion
type.We give three choices to the user: stationary, random, or smooth. The same design
we used in steps 3 and 4 is applicable here, so we adopt it for the motion type selection
also.Since we adopt the same design,we can ease into the coding phase.
Here’s the modified main class Ch5DrawShape:
step 4 test
step 5
design
step 5 code
import java.awt.*;
import java.util.*;
/*
Chapter 5 Sample Development: Color selection (Step 5)
The main class of the program.
*/
class Ch5DrawShape {
. . .
wu23399_ch05.qxd 12/14/06 17:51 Page 283
5.8 Sample Development—continued
public void start( ) {
DrawableShape shape1 = getShape();
canvas.addShape(shape1);
canvas.setMovement(inputMotionType());
canvas.setVisible(true);
canvas.start();
}
. . .
private DrawingBoard.Movement inputMotionType( ) {
System.out.print(Selection: Enter the Motion numbern +
 1 - Stationary (no movement) n +
 2 - Random Movement n +
 3 - Smooth Movement n );
int selection = scanner.nextInt();
DrawingBoard.Movement type;
switch (selection) {
case 1: type = DrawingBoard.Movement.STATIONARY;
break;
case 2: type = DrawingBoard.Movement.RANDOM;
break;
case 3: type = DrawingBoard.Movement.SMOOTH;
break;
default: type = DrawingBoard.Movement.SMOOTH;
break;
}
return type;
}
. . .
}
284 Chapter 5 Selection Statements
start
inputMotionType
No changes are required for the DrawableShape class,as the DrawingBoard class
is the one responsible for the shape movement.
wu23399_ch05.qxd 12/14/06 17:51 Page 284
Now we run the program multiple times and test all three motion types. From what
we have done, we can’t imagine the code we have already written in the earlier steps to
cause any problems;but if we are not careful,a slight change in one step could cause the
code developed from the earlier steps to stop working correctly (e.g.,erroneously reusing
data members in newly written methods).So we should continue to test all aspects of the
program diligently. After we are satisfied with the program, we proceed to the final step.
Step 6 Development: Finalize
We will perform a critical review of the program, looking for any unfinished method,
inconsistency or error in the methods, unclear or missing comments, and so forth. We
should also not forget to improve the program for cleaner code and better readability.
Another activity we can pursue in the final step is to look for extensions.
There are several interesting extensions we can make to the program. First is the
morphing of an object. In the current implementation, once the shape is selected, it will
not change. It would be more fun to see the shape changes; for example, the width and
height of the shape’s dimension can be set to vary while the shape is drawn. Another
interesting variation is to make a circle morph into a rectangle and morph back into a
circle. Second is the drawing of multiple shapes. Third is the variation in color while the
shape is drawn.Fourth is the drawing of a text (we“draw”a text on the Graphics context
just as we draw geometric shapes). You can make the text scroll across the screen from
right to left by setting the motion type of DrawingBoard to STATIONARY and updating
the center point value within our DrawableShape class. All these extensions are left as
exercises.
Summary 285
step 5 test
program
review
possible
extensions
• A selection control statement is used to alter the sequential flow of control.
• The if and switch statements are two types of selection control.
• The two versions of the if statement are if–then–else and if–then.
• A boolean expression contains conditional and boolean operators and
evaluates to true or false.
• Three boolean operators in Java are AND (), OR (||), and NOT (!).
• DeMorgan’s laws state !(P Q) and !P || !Q are equivalent and !(P || Q) and
!P  !Q are equivalent.
• Logical operators  and || are evaluated by using the short-circuit
evaluation technique.
• A boolean flag is useful in keeping track of program settings.
• An if statement can be a part of the then or else block of another if statement
to formulate nested if statements.
• Careful attention to details is important to avoid illogically constructed
nested if statements.
S u m m a r y
wu23399_ch05.qxd 12/14/06 17:51 Page 285
• When the equality symbol == is used in comparing the variables of reference
data type, we are comparing the addresses.
• The switch statement is useful for expressing a selection control based on
equality testing between data of type char, byte, short, or int.
• The break statement causes the control to break out of the surrounding
switch statement (note: also from other control statements introduced in
Chap. 6).
• The standard classes introduced in this chapter are
286 Chapter 5 Selection Statements
java.awt.Graphics
java.awt.Color
java.awt.Point
java.awt.Dimension
K e y C o n c e p t s
sequential execution
control statements
if statement
boolean expressions
relational operators
selection statements
nested if statements
increment and decrement operators
boolean operators
switch statements
break statements
defensive programming
content pane of a frame
enumerated constants
• The java.awt.Graphics class is used to draw geometric shapes.
• The java.awt.Color class is used to set the color of various GUI components.
• The java.awt.Point class is used to represent a point in two-dimensional space.
• The java.awt.Dimension class is used to represent a bounding rectangle of
geometric shapes and other GUI components.
• The enumerated constants provide type safety and increase the program
readability.
E x e r c i s e s
1. Indent the following if statements properly.
a. if (a == b) if (c == d) a = 1; else b = 1; else c = 1;
b. if (a == b) a = 1; if (c == d) b = 1; else c = 1;
c. if (a == b) {if (c == d) a = 1; b = 2; } else b = 1;
d. if (a == b) {
if (c == d) a = 1; b = 2; }
else {b = 1; if (a == d) d = 3;}
2. Which two of the following three if statements are equivalent?
a. if (a == b)
if (c == d) a = 1;
else b = 1;
wu23399_ch05.qxd 12/14/06 17:51 Page 286
b. if (a == b) {
if (c == d) a = 1; }
else b = 1;
c. if (a == b)
if (c == d) a = 1;
else b = 1;
3. Evaluate the following boolean expressions. For each of the following
expressions, assume x is 10, y is 20, and z is 30. Indicate which of the
following boolean expressions are always true and which are always false,
regardless of the values for x, y, or z.
a. x  10 || x  10
b. x  y  y  x
c. (x  y + z)  (x + 10 = 20)
d. z - y == x  Math.abs(y - z) == x
e. x  10  x  10
f. x  y || y  x
g. !(x  y + z) || !(x + 10 = 20)
h. !(x == y))  (x != y)  (x  y || y  x)
4. Express the following switch statement by using nested if statements.
switch (grade) {
case 10:
case 9: a = 1;
b = 2;
break;
case 8: a = 3;
b = 4;
break;
default: a = 5;
break;
}
5. Write an if statement to find the smallest of three given integers without
using the min method of the Math class.
6. Draw control flow diagrams for the following two switch statements.
Exercises 287
switch (choice) {
case 1: a = 0;
break;
case 2: b = 1;
break;
case 3: c = 2;
break;
default: d = 3;
break;
}
switch (choice) {
case 1: a = 0;
case 2: b = 1;
case 3: c = 2;
default: d = 3;
}
wu23399_ch05.qxd 12/14/06 17:51 Page 287
7. Write an if statement that prints out a message based on the following rules:
288 Chapter 5 Selection Statements
If the Total Points Are Message to Print
 100 You won a free cup of coffee.
 200 You won a free cup of coffee and a regular-size doughnut.
 300 You won a free cup of coffee and a regular-size
doughnut and a 12-oz orange juice.
 400 You won a free cup of coffee and a regular-size dough-
nut and a 12-oz orange juice and a combo breakfast.
 500 You won a free cup of coffee and a regular-size
doughnut and a 12-oz orange juice and a combo
breakfast and a reserved table for one week.
8. Rewrite the following if statement, using a switch statement.
selection = scanner.nextInt( );
if (selection == 0)
System.out.println(You selected Magenta);
else if (selection == 1)
System.out.println(You selected Cyan);
else if (selection == 2)
System.out.println(You selected Red);
else if (selection == 3)
System.out.println(You selected Blue);
else if (selection == 4)
System.out.println(You selected Green);
else
System.out.println(Invalid selection);
9. At the end of movie credits you see the year movies are produced in Roman
numerals, for example, MCMXCVII for 1997. To help the production staff
determine the correct Roman numeral for the production year, write an applet
or application that reads a year and displays the year in Roman numerals.
Roman Numeral Number
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
Remember that certain numbers are expressed by using a “subtraction,” for
example, IV for 4, CD for 400, and so forth.
wu23399_ch05.qxd 12/14/06 17:51 Page 288
10. Write a program that replies either Leap Year or Not a Leap Year, given a
year. It is a leap year if the year is divisible by 4 but not by 100 (for
example, 1796 is a leap year because it is divisible by 4 but not by 100). A
year that is divisible by both 4 and 100 is a leap year if it is also divisible by
400 (for example, 2000 is a leap year, but 1800 is not).
11. One million is 106
and 1 billion is 109
. Write a program that reads a power
of 10 (6, 9, 12, etc.) and displays how big the number is (Million, Billion,
etc.). Display an appropriate message for the input value that has no
corresponding word. The table below shows the correspondence between
the power of 10 and the word for that number.
Power of 10 Number
6 Million
9 Billion
12 Trillion
15 Quadrillion
18 Quintillion
21 Sextillion
30 Nonillion
100 Googol
12. Write a program RecommendedWeightWithTest by extending the
RecommendedWeight (see Exercise 8 on page 209). The extended program
will include the following test:
if (the height is between 140cm and 230cm)
compute the recommended weight
else
display an error message
13. Extend the RecommendedWeightWithTest program in Exercise 12 by
allowing the user to enter his or her weight and printing out the message You
should exercise more if the weight is more than 10 lb over the ideal weight
and You need more nourishment if the weight is more than 20 lb under the
recommended weight.
14. Employees at MyJava Lo-Fat Burgers earn the basic hourly wage of $7.25.
They will receive time-and-a-half of their basic rate for overtime hours. In
addition, they will receive a commission on the sales they generate while
tending the counter. The commission is based on the following formula:
Exercises 289
Sales Volume Commission
$1.00 to $99.99 5% of total sales
$100.00 to $299.99 10% of total sales
 $300.00 15% of total sales
Write an application that inputs the number of hours worked and the total
sales and computes the wage.
wu23399_ch05.qxd 12/14/06 17:51 Page 289
15. Using the DrawingBoard class, write a screensaver that displays a scrolling
text message. The text messages moves across the window, starting from the
right edge toward the left edge. Set the motion type to stationary, so the
DrawingBoard does not adjust the position. You have to adjust the text’s
position inside your DrawableShape.
16. Define a class called Triangle that is capable of computing the perimeter and
area of a triangle, given its three sides a, b, and c, as shown below. Notice
that side b is the base of the triangle.
b
c
a
290 Chapter 5 Selection Statements
Perimeter  a  b  c
Area  s(s  a
)(s  b
)(s  c
)

where s  
a 
2
b  c

The design of this class is identical to that for the Ch5Circle class from
Section 5.1. Define a private method isValid to check the validity of three
sides. If any one of them is invalid, the methods getArea and getPerimeter
will return the constant INVALID_DIMENSION.
17. Modify the Ch5RoomWinner class so the three dorm lottery cards are drawn
vertically. Make the code for drawing flexible by using the HEIGHT constant
in determining the placement of three cards.
Development Exercises
For the following exercises, use the incremental development methodology to
implement the program. For each exercise, identify the program tasks, create
a design document with class descriptions, and draw the program diagram.
Map out the development steps at the start. Present any design alternatives and
justify your selection. Be sure to perform adequate testing at the end of each
development step.
18. MyJava Coffee Outlet (see Exercise 25 from Chap. 3) decided to give
discounts to volume buyers. The discount is based on the following table:
Order Volume Discount
 25 bags 5% of total price
 50 bags 10% of total price
 100 bags 15% of total price
 150 bags 20% of total price
 200 bags 25% of total price
 300 bags 30% of total price
wu23399_ch05.qxd 12/14/06 17:51 Page 290
Each bag of beans costs $5.50. Write an application that accepts the number
of bags ordered and prints out the total cost of the order in the following
style:
Number of Bags Ordered: 173 - $ 951.50
Discount:
20% - $ 190.30
Your total charge is: $ 761.20
19. Combine Exercises 18 and 25 of Chap. 3 to compute the total charge
including discount and shipping costs. The output should look like the
following:
Number of Bags Ordered: 43 - $ 236.50
Discount:
5% - $ 11.83
Boxes Used:
1 Large - $1.80
2 Medium - $2.00
Your total charge is: $ 228.47
Note: The discount applies to the cost of beans only.
20. You are hired by Expressimo Delivery Service to develop an application
that computes the delivery charge. The company allows two types of
packaging—letter and box—and three types of service—Next Day Priority,
Next Day Standard, and 2-Day. The following table shows the formula for
computing the charge:
Exercises 291
Package Next Day Next Day
Type Priority Standard 2-Day
Letter $12.00,up to 8 oz $10.50,up to 8 oz Not available
Box $15.75 for the first $13.75 for the first $7.00 for the first
pound.Add $1.25 pound.Add $1.00 pound. Add $0.50
for each additional for each additional for each additional
pound over the first pound over the first pound over the first
pound. pound. pound.
The program will input three values from the user: type of package, type of
service, and weight of the package.
wu23399_ch05.qxd 12/14/06 17:51 Page 291
21. Ms. Latte’s Mopeds ‘R Us rents mopeds at Monterey Beach Boardwalk. To
promote business during the slow weekdays, the store gives a huge discount.
The rental charges are as follows:
292 Chapter 5 Selection Statements
Moped Type Weekday Rental Weekend Rental
50cc Mopette $15.00 for the first 3 h, $30.00 for the first 3 h,
$2.50 per hour after the $7.50 per hour after the
first 3 h. first 3 h.
250cc Mohawk $25.00 for the first $35.00 for the first 3 h,
3 h,$3.50 per hour after $8.50 per hour after the
the first 3 h. first 3 h.
Write a program that computes the rental charge, given the type of moped,
when it is rented (either weekday or weekend), and the number of hours
rented.
22. Write an application program that teaches children how to read a clock. Use
JOptionPane to enter the hour and minute. Accept only numbers between
0 and 12 for hour and between 0 and 59 for minute. Print out an appropriate
error message for an invalid input value. Draw a clock that looks something
like this:
To draw a clock hand, you use the drawLine method of the Graphics class.
The endpoints of the line are determined as follows:
(ox  K cos , oy  K sin )
(ox, oy)
Note: We subtract here because
the y value in pixel coordinates
for windows increases in the
downward direction.
wu23399_ch05.qxd 12/14/06 17:51 Page 292
The value for constant K determines the length of the clock hand. Make the
K larger for the minute hand than for the hour hand. The angle  is expressed
in radians. The angle min of the minute hand is computed as
(90  Minute 6.0) 
1

80

and the angle hr of the hour hand is computed as
90 
Hour  
M
6
i
0
n
.
u
0
te
 30.0

1

80

where Hour and Minute are input values. The values 6.0 and 30.0 designate
the degrees for 1 min and 1 h (i.e., the minute hand moves 6 degrees in
1 min and the hour hand moves 30.0 degrees in 1 h). The factor 180
converts a degree into the radian equivalent.
You can draw the clock on the content pane of a frame window by
getting the content pane’s Graphic object as described in the chapter. Here’s
some sample code:
import javax.swing.*;
import java.awt.*; //for Graphics
...
JFrame win;
Container contentPane;
Graphics g;
...
win = new JFrame();
win.setSize(300, 300);
win.setLocation(100,100);
win.setVisible(true);
...
contentPane = win.getContentPane();
g = contentPane.getGraphics();
g.drawOval(50,50,200,200);
23. Extend the application in Exercise 22 by drawing a more realistic, better-
looking clock, such as this one:
3
9
12
6
Exercises 293
wu23399_ch05.qxd 12/14/06 17:51 Page 293
24. After starting a successful coffee beans outlet business, MyJava Coffee
Outlet is now venturing into the fast-food business. The first thing the
management decides is to eliminate the drive-through intercom. MyJava
Lo-Fat Burgers is the only fast-food establishment in town that provides a
computer screen and mouse for its drive-through customers. You are hired as
a freelance computer consultant. Write a program that lists items for three
menu categories: entree, side dish, and drink. The following table lists the
items available for each entry and their prices. Choose appropriate methods
for input and output.
Entree Side Dish Drink
Tofu Burger $3.49 Rice Cracker $0.79 Cafe Mocha $1.99
Cajun Chicken $4.59 No-Salt Fries $0.69 Cafe Latte $1.99
Buffalo Wings $3.99 Zucchini $1.09 Espresso $2.49
Rainbow Fillet $2.99 Brown Rice $0.59 Oolong Tea $0.99
294 Chapter 5 Selection Statements
wu23399_ch05.qxd 12/14/06 17:51 Page 294
Repetition Statements
O b j e c t i v e s
After you have read and studied this chapter,you should be able to
• Nest a loop repetition statement inside
another repetition statement.
• Choose the appropriate repetition control
statement for a given task.
• (Optional) Write simple recursive methods.
• Format output values by using the Formatter
class.
295
• Implement repetition control in a program
using while statements.
• Implement repetition control in a program
using do–while statements.
• Implement a generic loop-and-a-half
repetition control statement.
• Implement repetition control in a program
using for statements.
6
wu23399_ch06.qxd 12/14/06 17:53 Page 295
he selection statements we covered in Chapter 5 alter the control flow of a program.
In this chapter we will cover another group of control statements called repetition
statements. Repetition statements control a block of code to be executed for a fixed
number of times or until a certain condition is met. We will describe Java’s three
repetition statements: while, do–while, and for. Finally, in optional Section 6.11, we
will describe recursive methods. A recursive method is a method that calls itself.
Instead of a repetition statement, a recursive method can be used to program the
repetition control flow.
6.1 The while Statement
Suppose we want to compute the sum of the first 100 positive integers 1, 2, . . . , 100.
Here’s how we compute the sum, using a while statement:
int sum = 0, number = 1;
while (number = 100) {
sum = sum + number;
number = number + 1;
}
Let’s analyze the while statement. The statement follows the general format
while ( boolean expression )
statement
where statement is either a single statement or a compound statement. The
statement of the sample while statement is a compound statement and there-
fore has the left and right braces. Repetition statements are also called loop state-
ments, and we characterize the statement as the loop body. Figure 6.1 shows how
this while statement corresponds to the general format. As long as the boolean
expression is true, the loop body is executed. Figure 6.2 is a diagram showing the
control flow of the sample code.
Let’s modify the loop so this time we keep on adding the numbers 1, 2, 3, and
so forth, until the sum becomes more than 1,000,000. Here’s how we write the while
statement:
int sum = 0, number = 1;
while ( sum = 1000000 ) {
sum = sum + number;
number = number + 1;
}
296 Chapter 6 Repetition Statements
I n t r o d u c t i o n
T
while state-
ment syntax
loop body
recursive
method
repetition
statements
wu23399_ch06.qxd 12/14/06 17:53 Page 296
Notice how the boolean expression is modified, and it is the only part of the while
statement that is modified.
Let’s try another example. This time, we compute the product of the first
20 odd integers. (Note: The ith odd integer is 2 * i – 1. For example, the fourth odd
integer is 2 * 4 – 1 = 7.)
int product = 1, number = 1, count = 20, lastNumber;
lastNumber = 2 * count - 1;
while (number = lastNumber) {
product = product * number;
number = number + 2;
}
The first and the third sample while statements are called count-controlled
loops because the loop body is executed for a fixed number of times (as if we were
counting).
6.1 The while Statement 297
count-
controlled loop
number = 100 ) {
while (
sum = sum + number;
number = number + 1;
}
Boolean Expression
Statement
(loop body)
Figure 6.1 Correspondence of the example while statement of the general format.
Figure 6.2 A diagram showing the control flow of a while statement.
true
false
number = 100?
sum = sum + number;
number = number + 1;
int sum = 0, number = 1;
wu23399_ch06.qxd 12/14/06 17:53 Page 297
Improving User Interface with a Loop
Now let’s study how the repetition control in the program will improve the user in-
terface of the program. In earlier sample programs, we assumed the input data were
valid. The programs we have written may produce wrong results or simply stop run-
ning if the user enters an invalid value. Assuming that the input values are valid
makes the writing of programs easier because we do not have to write code to han-
dle the invalid values. Although it is easier for us to write such programs, it would
be an inferior interface from the user’s standpoint. Requiring the user to make no
mistake in entering input values is too restrictive and not user-friendly. We need to
develop programs that are more user-friendly. Imagine you successfully entered
19 values, but on the 20th input value, you mistyped. A user-hostile program would
stop, and you would have to run the program again. A more user-friendly program
would allow you to reenter a correct 20th value.
All we could have done using a selection statement was either to print out an
error message or to set a default value if the user enters an invalid value. In the
inputShapeType method of the Chapter 5 sample development, for example, if the
user enters any invalid value, we set the shape type to ellipse. Instead of quitting
the program after displaying an error message or continuing the program with a de-
fault value, it would be better in general to allow the user to reenter the value until
the correct value is entered. We need a repetition control to achieve this.
Let’s look at an example. Suppose we want to input a person’s age, and the
value must be between 0 and 130. We know the age cannot be negative, so the age
input must be greater than or equal to 0. We set the upper bound to 130 to take into
account the possibility of some long-living human beings in a remote hamlet in
Timbuktu. Let’s say we will let the user enter the age until a valid age is entered. We
can code this repetition control, using a while statement:
Scanner scanner = new Scanner(System.in);
int age;
System.out.print(Your Age (between 0 and 130): );
age = scanner.nextInt();
while (age  70 || age  130) {
System.out.println(
An invalid age was entered. Please try again.);
System.out.print (Your Age (between 0 and 130): );
age = scanner.nextInt();
}
Notice that we included the statements
System.out.print(Your Age (between 0 and 130): );
age = scanner.nextInt();
to input the age before the while statement. Without this input statement, the vari-
able age will not have a value when the boolean expression is evaluated for the very
298 Chapter 6 Repetition Statements
wu23399_ch06.qxd 12/14/06 17:53 Page 298
first time. This reading of a value before the testing is done is called a priming read.
We will discuss this issue of priming read further in Section 6.4.
As the second example, let’s modify the inputShapeType method from Sec-
tion 5.6. To refresh our memory, here’s the original code:
private DrawableShape.Type inputShapeType( ) {
System.out.print(Selection: Enter the Shape numbern +
 1 - Ellipse n +
 2 - Rectangle n +
 3 - Rounded Rectangle n);
int selection = scanner.nextInt();
DrawableShape.Type type;
switch (selection) {
case 1: type = DrawableShape.Type.ELLIPSE;
break;
case 2: type = DrawableShape.Type.RECTANGLE;
break;
case 3: type = DrawableShape.Type.ROUNDED_RECTANGLE;
break;
default: type = DrawableShape.Type.ELLIPSE;
break;
}
return type;
}
To allow the user to reenter the value until the valid entry is made, we can modify
the method to the following:
private int inputShapeType( ) {
int selection = getSelection();
DrawableShape.Type type;
switch (selection) {
case 1: type = DrawableShape.Type.ELLIPSE;
break;
case 2: type = DrawableShape.Type.RECTANGLE;
break;
case 3: type = DrawableShape.Type.ROUNDED_RECTANGLE;
break;
default: System.out.println
(Internal Error: Proceed with Default);
type = DrawableShape.Type.ELLIPSE;
break;
6.1 The while Statement 299
priming read
getSelection is defined
after this method.
This default case should
never happen if getSelection
is implemented correctly.We
put this here to catch any
internal coding error.
wu23399_ch06.qxd 12/14/06 17:53 Page 299
}
return type;
}
private int getSelection( ) {
int selection;
System.out.print(Selection: Enter the Shape numbern +
 1 - Ellipse n +
 2 - Rectangle n +
 3 - Rounded Rectangle n);
selection = scanner.nextInt();
while (selection  1 || selection  3) {
System.out.println(
An invalid age was entered. Please try
again.n);
System.out.print(Selection: Enter the Shape
numbern +
 1 - Ellipse n +
 2 - Rectangle n +
 3 - Rounded Rectangle n);
selection = scanner.nextInt();
}
return selection;
}
The next example keeps reading in integers and computes their running sum
until a negative number is entered.
int sum = 0; number;
Scanner scanner = new Scanner(System.in);
System.out.print(Enter integer );
number = scanner.nextInt();
while (number = 0) {
sum = sum + number;
System.out.print(Enter integer );
number = scanner.nextInt();
}
The previous three sample while statements are called sentinel-controlled
loops. With a sentinel-controlled loop, the loop body is executed repeatedly until
any one of the designated values, called a sentinel, is encountered. The sentinels for
the three examples, respectively, are any value between 0 and 130, any value from
1 to 3, and any negative number.
300 Chapter 6 Repetition Statements
sentinel-
controlled loop
wu23399_ch06.qxd 12/14/06 17:53 Page 300
Sample Program with a Loop
Let’s write a short sample program that illustrates the use of a while statement. It is
a well-known fact that students in college do not get enough sleep, some studying
hard while others are enjoying life too much. Which dorm they live in also makes a
huge difference, so let’s develop a program that determines the average sleeping
time of the residents in a given dorm. This information can be made available on the
housing office website so the students can make an informed decision on which
dorm to choose for the next academic year.
Using Scanner, first we will input the dorm name. Then we loop and input the
length of sleep of the residents until the input value of zero is entered. When the
input is done, the average sleep time is displayed. We use zero as a sentinel value in-
stead of a negative number such as –1 because we do not want to consider a zero as
a valid entry. Here’s the program listing:
6.1 The while Statement 301
/*
Chapter 6 Sample Program: Sleep Statistics for Dorm Residents
File: Ch6SleepStatistics.java
*/
import java.text.*;
import java.util.*;
class Ch6SleepStatistics {
private Scanner scanner;
public static void main (String[] args) {
Ch6SleepStatistics prog = new Ch6SleepStatistics( );
prog.start();
}
public Ch6SleepStatistics() {
scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
}
public void start( ) {
double sleepHour, sum = 0;
int cnt = 0;
//enter the dorm name
System.out.print(Dorm name: );
String dorm = scanner.next();
//Loop: get hours of sleep for each resident
// until 0 is entered.
sleepHour = getDouble(Enter sleep hours (0 - to stop:);
main
start
Constructor
wu23399_ch06.qxd 12/14/06 17:53 Page 301
while (sleepHour != 0) {
sum += sleepHour;
cnt++;
sleepHour = getDouble(Enter sleep hours (0 - to stop):);
}
if (cnt == 0) {
System.out.println (No Data Entered);
} else {
DecimalFormat df = new DecimalFormat(0.00);
System.out.println(
Average sleep time for  +
dorm +  is nn  +
df.format(sum/cnt) +  hours.);
}
}
private double getDouble(String message) {
double result;
System.out.print(message);
result = scanner.nextDouble();
return result;
}
}
302 Chapter 6 Repetition Statements
getDouble
Finding the Greatest Common Divisor
Let’s close this section with a slightly more complicated example of using a loop
statement. In Section 5.4, we defined the equals method for the Fraction class. We
indicated that the fully functional equals method needs to call another method to re-
duce a fraction to its simplest from (e.g., the simplest form of 16/32 is 1/2). To sim-
plify a fraction, we need to find the greatest common divisor of its numerator and
denominator. For example, the greatest common divisor of 16 and 32 is 16. Divid-
ing both the numerator and the denominator by their greatest common denominator
will reduce the fraction to its simplest form. Here we will define a method that
returns the greatest common divisor of two given arguments. (Note: We will de-
velop a full definition of the Fraction class in Chapter 7 when we introduce additional
concepts on programmer-defined classes.)
We will first provide a brute-force solution (inelegant) and then a clever solu-
tion based on the Euclidean algorithm (elegant). The brute-force approach derives
wu23399_ch06.qxd 12/14/06 17:53 Page 302
the solution by applying the definition of greatest common divisor directly. Given
two positive integers M and N, where M  = N, we find their greatest common divi-
sor by dividing M and N with values from 1 to M. The last integer that divided both
M and N perfectly (i.e., there is no remainder), is the greatest common divisor.
Consider 24 and 36, for example. The numbers that divide 24 and 36 perfectly are
1, 2, 3, 4, 6, and 12. So the greatest common divisor is 12. The fraction 24/36 is
reduced to its simplest form 2/3 by dividing 24 and 36 by 12. We can see if a num-
ber j divides another number i perfectly by using the modulo arithmetic. If i % j ==
0, then j divides i perfectly because the remainder of the division is 0.
Here’s the brute-force method:
public int gcd_bruteforce(int m, int n) {
//assume m, n = 1
int last = Math.min(m, n);
int gcd;
int i = 1;
while (i = last) {
if (m % i == 0  n % i == 0) {
gcd = i;
}
i++;
}
return gcd;
}
Now let’s study an elegant solution based on the Euclidean algorithm. We
begin with an example. Consider two positive integers 44 and 16. We will use the
notation gcd(a, b) to stand for the greatest common divisor of a and b. Notice that
gcd(44, 16) = 4. Here’s how the Euclidean algorithm works. First divide 44 by 16.
The remainder is 12. We have the relation
44 = 2 * 16 + 12
From this, we can conclude that the greatest common divisor G that divides 44 and
16 must also divide 12. If it doesn’t, then we get a contradiction. If a number G can
divide 16 perfectly but cannot divide 12 perfectly, then 44 % G = (2*16 + 12) % G
will result in a nonzero value. This is a contradiction. So now we can reduce the
problem of finding gcd(44, 16) to gcd(16, 12). We repeat the process.
16 = 1 * 12 + 4
Now we reduce the problem to gcd(12, 4). Since
12 = 3 * 4 + 0
6.1 The while Statement 303
wu23399_ch06.qxd 12/14/06 17:53 Page 303
shows no remainder, we finish the process and return the answer 4 as the
greatest common divisor. The sequence of reduction is gcd(44, 16) = gcd(16, 12) =
gcd(12, 4) = 4.
How do we translate this concept into a working code? Let’s map out the
sequence of reductions graphically:
From this diagram, we see that M at one stage becomes N in the next stage and the
remainder R becomes M in the next stage. We repeat this process until the remain-
der becomes 0. The value of M (4 in this example) at the end of the repetition is the
greatest common divisor. Here’s the gcd method that implements this idea:
public int gcd(int m, int n) {
//it doesn't matter which of n and m is bigger
//this method will work fine either way
//assume m,n = 1
int r = n % m;
while (r !=0) {
n = m;
m = r;
r = n % m;
}
return m;
}
Here’s how we trace the repetition:
gcd(44, 16)
gcd(16, 12)
gcd(12, 4)
44 % 16  12
16 % 12  4
12 % 4  0
N M R  N % M
304 Chapter 6 Repetition Statements
Repetition
Count n m r
0 44 16 12
1 16 12 4
2 12 4 0
The first column indicates the number of times the while loop is executed. So the
first row shows the values of n, m, and r after zero repetitions, that is, before the while
statement is executed. The third row shows the values after the second repetition is
wu23399_ch06.qxd 12/14/06 17:53 Page 304
completed. At the point where the third repetition is attempted, the value of r is 0, so
the while loop is terminated and the value of m, which is 4, is returned.
The two versions of finding the greatest common denominator produce the
correct results. If they both produce the same results, which version shall we prefer?
The brute-force method is probably a lot easier to understand, at least initially, be-
cause it reflects the definition of greatest common divisor clearly. We always prefer
the one that is clearer and easier to understand, but only when their performances
are relatively the same. In this example, the Euclidean gcd method far outperforms
the gcd_bruteforce method. In other words, the Euclidean gcd method finds the
solution much faster than gcd_bruteforce. And the gap widens dramatically when
the values of M become large.We will analyze the performance of these two meth-
ods experimentally by recording their execution times in Section 6.10.
6.2 Pitfalls in Writing Repetition Statements 305
1. Write a while statement to add numbers 11 through 20. Is this a count-
controlled or sentinel-controlled loop?
2. Write a while statement to read in real numbers and stop when a negative
number is entered. Is this a count-controlled or sentinel-controlled loop?
6.2 Pitfalls in Writing Repetition Statements
No matter what you do with the while statement (and other repetition statements),
make sure that the loop will eventually terminate. Watch out for an infinite loop such
as this one:
int product = 0;
while (product  500000) {
product = product * 5;
}
Do you know why this is an infinite loop? The variable product is multiplied by 5
in the loop body, so the value for product should eventually become larger than
500000, right? Wrong. The variable product is initialized to 0, so product remains 0.
The boolean expression product  500000 will never be false, and therefore this
while statement is an infinite loop. You have to make sure the loop body contains a
statement that eventually makes the boolean expression false.
Here’s another example of an infinite loop:
int count = 1;
while (count != 10) {
count = count + 2;
}
Since the variable count is initialized to 1 and the increment is 2, count will
never be equal to 10. Note: In theory, this while statement is an infinite loop, but
infinite loop
wu23399_ch06.qxd 12/14/06 17:53 Page 305
in programming languages other than Java, this loop will eventually terminate
because of an overflow error. An overflow error will occur if you attempt to
assign a value larger than the maximum value the variable can hold. When an
overflow error occurs, the execution of the program is terminated in almost all
programming languages. With Java, however, an overflow will not cause program
termination. When an overflow occurs in Java, a value that represents infinity
(IEEE 754 infinity, to be precise) is assigned to a variable and no abnormal ter-
mination of a program will occur. Also, in Java an overflow occurs only with
float and double variables; no overflow will happen with int variables. When you
try to assign a value larger than the maximum possible integer that an int variable
can hold, the value “wraps around” and becomes a negative value.
Whether the loop terminates or not because of an overflow error, the logic of the
loop is still an infinite loop, and we must watch out for it. When you write a loop, you
must make sure that the boolean expression of the loop will eventually become false.
Another pitfall for you to avoid is the using of real numbers for testing and
increment. Consider the following two loops:
//Loop 1
double count = 0.0;
while (count != 1.0)
count = count + 0.333333333333333;
//there are fifteen 3s
//Loop 2
double count = 0.0;
while (count != 1.0)
count = count + 0.3333333333333333;
//there are sixteen 3s
The second while terminates correctly, but the first while is an infinite loop. Why the
difference? Because only an approximation of real numbers can be stored in a com-
puter. We know in mathematics that

1
3
  
1
3
  
1
3

is equal to 1. However, in a computer, an expression such as
1.0/3.0 + 1.0/3.0 + 1.0/3.0
may or may not get evaluated to 1.0, depending on how precise the approximation is.
The problem here is not that the number 1/3 is a repeating decimal. A decimal
number such as 0.1 cannot be stored precisely in a computer memory either. Con-
sider the following example:
double count = 0.0;
while (count != 1.0) {
count = count + 0.10;
}
306 Chapter 6 Repetition Statements
overflow error
imprecise loop
counter
wu23399_ch06.qxd 12/14/06 17:53 Page 306
This repetition statement looks simple enough. We initialize count to 0.0 and re-
peatedly add 0.10 to it, so after 10 repetitions, the loop should terminate. Wrong.
The counter variable count never becomes equal to 1.0. The closest it gets is
0.9999999999999999. Let’s change the loop to
double count = 0.0;
while (count = 1.0) {
count = count + 0.10;
System.out.println(count);
}
so we can see the values assigned to count. Here’s the output from this code:
0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999
1.0999999999999999
As these examples illustrate, we should avoid using real numbers as counter vari-
ables because of the imprecision.
6.2 Pitfalls in Writing Repetition Statements 307
Another thing to watch out for in writing a loop is the off-by-1 error. Suppose
we want to execute the loop body 10 times. Does the following code work?
count = 1;
while (count  10 ) {
...
count++;
}
No, the loop body is executed 9 times. How about the following code?
count = 0;
while (count = 10 ) {
...
count++;
}
Avoid using real numbers for counter variables as much as possible.If you use
them,then be aware of the pitfall and ensure that the loop terminates.
off-by-1 error
wu23399_ch06.qxd 12/14/06 17:53 Page 307
No, this time the loop body is executed 11 times. The correct while loop is
count = 0;
while (count  10 ) {
...
count++;
}
or
count = 1;
while (count = 10 ) {
...
count++;
}
Yes, we can write the desired loop as
count = 1;
while (count != 10 ) {
...
count++;
}
but this condition for stopping the count-controlled loop is dangerous. We already
mentioned about the potential trap of an infinite loop. In summary,
308 Chapter 6 Repetition Statements
Watch out for the off-by-1 error (OBOE).
To show you just how commonly the off-by-1 error occurs in everyday life, con-
sider the following two questions.When you want to put a fencepost every 10 ft,
how many posts do you need for a 100-ft fence? If it takes 0.5 s for an elevator to
rise one floor, how long does it take to reach the fourth floor from the first level?
The answers that come immediately are 10 posts and 2 s, respectively. But after a
little more thought, we realize the correct answers are 11 posts (we need the final
post at the end) and 1.5 s (there are three floors to rise to reach the fourth floor
from the first level).
Another common mistake made by beginning programmers is the inclusion of
avoidable test in a loop. Consider the following loop statement:
int oddSum = 0;
int evenSum = 0;
int num = 1;
wu23399_ch06.qxd 12/14/06 17:53 Page 308
while (num  1001) {
if (num / 2 == 0) { //even #
evenSum = evenSum + num;
} else { //odd #
oddSum = oddSum + num;
}
num = num + 2;
}
The code computes the sum of even numbers and the sum of odd numbers between
1 and 1000, inclusive. To compute the two sums, the if test is executed 1000 times.
Is it necessary? No. We can compute the two sums more efficiently by writing two
separate loops:
int oddSum = 0;
int evenSum = 0;
int num = 1;
while (num  1001) {
oddSum = oddSum + num;
num = num + 2;
}
num = 2;
while (num  1001) {
evenSum = evenSum + num;
num = num + 2;
}
We can improve the code even further by usign only one loop as follows:
int oddSum = 0;
int evenSum = 0;
int num = 1;
while (num  1001) {
oddSum = oddSum + num;
evenSum = evenSum + (num + 1);
num = num + 2;
}
6.2 Pitfalls in Writing Repetition Statements 309
This test can be avoided by
writing two loops.
wu23399_ch06.qxd 12/14/06 17:53 Page 309
And here are the points for you to remember in writing a loop.
310 Chapter 6 Repetition Statements
The checklist for the repetition control:
1. Make sure the loop body contains a statement that will eventually cause the
loop to terminate.
2. Make sure the loop repeats exactly the correct number of times.
3. If you want to execute the loop body N times,then initialize the counter to 0
and use the test condition counter  N or initialize the counter to 1 and use the
test condition counter  N.
1. Which of the following is an infinite loop?
a. int sum = 0, i = 0;
while ( i = 0 ) {
sum += i;
i++;
}
b. int sum = 0, i = 100;
while ( i != 0 ) {
sum += i;
i--;
}
2. For each of the following loop statements, determine the value of sum after
the loop is executed.
a. int count = 0, sum = 0;
while ( count  10 ) {
sum += count;
count++;
}
b. int count = 1, sum = 0;
while ( count = 30 ) {
sum += count;
count += 3;
}
c. int count = 0, sum = 0;
while ( count  20 ) {
sum += 3*count;
count += 2;
}
wu23399_ch06.qxd 12/14/06 17:53 Page 310
6.3 The do–while Statement
The while statement is characterized as a pretest loop because the test is done before
execution of the loop body. Because it is a pretest loop, the loop body may not be ex-
ecuted at all. The do–while is a repetition statement that is characterized as a posttest
loop. With a posttest loop statement, the loop body is executed at least once.
The general format for the do–while statement is
do
statement
while (boolean expression ) ;
The statement is executed until the boolean expression becomes false.
Remember that statement is either a single statement or a compound state-
ment. We will adopt the same policy for the if statement; that is, we will use
the syntax of compound statement even if there is only one statement in the
loop body. In other words, we will use the left and right braces even if the loop body
contains only one statement.
Let’s look at a few examples. We begin with the second example from Sec-
tion 6.1, which adds the whole numbers 1, 2, 3, . . . until the sum becomes larger
than 1,000,000. Here’s the equivalent code in a do–while statement:
int sum = 0, number = 1;
do {
sum += number;
number++;
} while ( sum = 1000000 );
Figure 6.3 shows how this do–while statement corresponds to the general format,
and Figure 6.4 is a diagram showing the control flow of this do–while statement.
Let’s rewrite the routine that inputs a person’s age by using the do–while state-
ment. Here’s our first attempt:
do {
System.out.print(Your Age (between 0 and 130): );
age = scanner.nextInt();
} while (age  0 || age  130);
It works, but unlike the version using the while statement, the code does not display
an error message. The user could be puzzled as to why the input is not accepted.
Suppose the user tries to enter 130 but actually enters 139 unintentionally. Without
an error message to inform the user that the input was invalid, he or she may won-
der why the program is asking again for input. A program should not be confusing
to the user. We must strive for a program with a user-friendly interface.
6.3 The do–while Statement 311
do-while
statement
do–while
syntax
pretest loop
posttest loop
wu23399_ch06.qxd 12/14/06 17:53 Page 311
To display an error message, we rewrite the do–while statement as
do {
System.out.print(Your Age (between 0 and 130): );
age = scanner.nextInt();
if (age  0 || age  130) {
System.out.println(
An invalid age was entered. Please try again.);
} while (age  0 || age  130);
This code is not as good as the version using the while statement. Do you know
why? This do–while statement includes an if statement inside its loop body. Since
the loop body is executed repeatedly, it is important not to include any extraneous
statements. The if statement is repeating the same boolean expression of the
do–while. Duplicating the testing conditions tends to make the loop statement
harder to understand. For this example, we can avoid the extra test inside the loop
312 Chapter 6 Repetition Statements
while ( sum = 1000000 );
}
do {
sum += number;
number++; Boolean Expression
Statement
(loop body)
Figure 6.3 Correspondence of the example do–while statement to the general format.
true
false
sum = 1000000?
sum += number;
number++;
int sum = 0,
number = 1;
Figure 6.4 A diagram showing the control flow of the do–while statement.
wu23399_ch06.qxd 12/14/06 17:53 Page 312
body and implement the control flow a little more clearly by using a while state-
ment. In general, the while statement is more frequently used than the do–while
statement. However, the while statement is not universally better than the do–while
statement. It depends on a given task, and our job as programmers is to use the most
appropriate one. We choose the repetition statement that implements the control
flow clearly, so the code is easy to understand.
When you have multiple conditions to stop the loop and you need to execute
different responses to each of the multiple conditions, then the use of boolean
variables often clarifies the meaning of the loop statement. Consider the following
example. Suppose we need to compute the sum of odd integers entered by the user.
We will stop the loop when the sentinel value 0 is entered, an even integer is en-
tered, or the sum becomes larger than 1000. Without using any boolean variables,
we can write this loop as follows:
sum = 0;
do {
System.out.print(Enter integer: );
num = scanner.nextInt();
if (num == 0) { //sentinel
System.out.print(Sum =  + sum);
} else if (num % 2 == 0) //invalid data
System.out.print(Error: even number was entered);
} else {
sum += num;
if (sum  1000) { //pass the threshold
System.out.print(Sum became larger than 1000);
}
}
} while ( !(num % 2 == 0 || num == 0 || sum  1000) );
The ending condition is tricky. We need to stop the loop if any one of the three
conditions num % 2 == 0, num == 0, or sum  1000 is true. So we repeat the loop
when none of the three conditions are true, which is expressed as
!(num % 2 == 0 || num == 0 || sum  1000)
We can also state the condition as
do {
...
} while( num % 2 != 0  num != 0  sum = 1000 );
which means “repeat the loop while num is odd and num is not 0 and sum is less
than or equal to 1000.” Regardless of the method used, the test conditions are
duplicated inside the loop body and in the boolean expression.
6.3 The do–while Statement 313
boolean vari-
able and loop
Note:
!(a || b) is equal to (!a !b)
wu23399_ch06.qxd 12/14/06 17:53 Page 313
Set the variable to
false so the loop
terminates.
Now, by using a boolean variable, the loop becomes
314 Chapter 6 Repetition Statements
Note: continue is a
reserved word in Java,
while repeat is not.
boolean repeat = true;
sum = 0;
do {
System.out.print(Enter integer: );
num = scanner.nextInt();
if (num % 2 == 0) { //invalid data
System.out.print(Error: even number was entered);
repeat = false;
} else if (num == 0) { //sentinel
System.out.print(Sum =  + sum);
repeat = false;
} else {
sum += num;
if (sum  1000) { //pass the threshold
System.out.print(Sum became larger than 1000);
repeat = false;
}
}
} while ( repeat );
This loop eliminates duplicate tests. The use of boolean variables is helpful in mak-
ing loop statements readable, especially when the loop has multiple stop conditions.
As the last example of this section, here’s the gcd method implemented by using
the do–while statement (we’ll call it gcd_do to differentiate it from other versions):
public int gcd_do(int m, int n) {
//it doesn't matter which of n and m is bigger
//this method will work fine either way
//assume m,n = 1
int r;
do {
r = n % m;
n = m;
m = r;
} while (r != 0);
return n; //NOTE: we're returning n, not m
// because m == r == 0 after the loop
}
wu23399_ch06.qxd 12/14/06 17:53 Page 314
6.4 Loop-and-a-Half Repetition Control 315
1. Write a do–while loop to compute the sum of the first 30 positive odd integers.
2. Rewrite the following while loops as do–while loops.
a. int count = 0, sum = 0;
while ( count  10 ) {
sum += count;
count++;
}
b. int count = 1, sum = 0;
while ( count = 30 ) {
sum += count;
count += 3;
}
loop-and-a-half
control
6.4 Loop-and-a-Half Repetition Control
When we compare the while and do–while repetition control, we realize the key dif-
ference is the position of the testing relative to the loop body. The while loop tests
the terminating condition before the loop body, but the do–while tests the terminat-
ing condition after the loop body. What happens when we want to test the terminat-
ing condition right in the middle of the loop body? Such repetition control can be
characterized as a loop-and-a-half control because only the top half of the loop body
is executed for the last repetition. Do we ever need such a looping statement?
Consider the following while loop with the priming read:
String name;
System.out.print(Your name: );
name = scanner.next();
while (name.length() == 0) {
System.out.println(Invalid entry.  +
You must enter at least one character.);
System.out.print(Your name: );
name = scanner.next();
}
Because the while loop tests the terminating condition at the beginning, we must
place some statements before the loop to ensure the condition can be evaluated. The
same statements are repeated inside the loop, so the terminating condition can be
evaluated correctly after each repetition. This duplication of the statements can
become tedious depending on what is to be duplicated. We can avoid the duplication
wu23399_ch06.qxd 12/14/06 17:53 Page 315
of code with the loop-and-a-half structure. Java does not support any special re-
served word for the loop-and-a-half repetition control. Rather, we implement it
using the while, if, and break reserved words. Here’s how we express the sample
priming read while loop in a loop-and-a-half format:
String name;
while (true) {
System.out.print(Your name: );
name = scanner.next();
if (name.length() == 0) break;
System.out.println(Invalid entry.  +
You must enter at least one character. );
}
We have seen the use of the break statement in Chapter 5. Execution of the
break statement causes the control to jump out of the switch statement. We can in
fact use the break statement with any control statement. In this example, the break
statement causes the control to jump out of the while statement. Since it is executed
when the if test is true, the String variable name contains at least one character. If
the test fails, the next statement is executed and the control loops back to the top of
the while loop. Expressing this control flow in a flowchart will result in the one
shown in Figure 6.5.
There are two concerns when we use the loop-and-a-half control. The first is
the danger of an infinite loop. Notice the boolean expression of the while state-
ment is simply true, which, of course, will always evaluate to true. So, if we forget
to include an if statement to break out of the loop, it will end up in an infinite loop.
316 Chapter 6 Repetition Statements
If the test evaluates
to true, then jump
out of the loop.
false
true
name.length()  0?
name = scanner.next();
System.out.println(...);
Figure 6.5 A diagram showing the control flow of a loop-and-a-half statement.
wu23399_ch06.qxd 12/14/06 17:53 Page 316
The second concern is the complexity of multiple exit points. It is possible to write
a loop-and-a-half statement with multiple break statements, something like this:
while (true) {
...
if (condition 1) break;
...
if (condition 2) break;
...
if (condition 3) break;
...
}
It gets tricky to write a correct control loop with multiple exit points. One of the
frequently cited software engineering practices for reliable code is to enforce the
one-entry one-exit control flow. In other words, there is one entry point to the loop
and one exit point from the loop. With the standard while and do–while with no
break statements inside the loop, we have this one-entry one-exit control flow. A
loop-and-a-half control with multiple break statements, however, violates it.
If we watch out for these two points, a loop-and-a-half control can be quite
handy and can make the code more readable. Here are the things to remember in
using the loop-and-a-half control.
6.4 Loop-and-a-Half Repetition Control 317
one-entry
one-exit
control
The checklist for the loop-and-a-half control:
1. To avoid an infinite loop, make sure the loop body contains at least one if
statement that breaks out of the loop.
2. To keep the control simple and easy to read, avoid using multiple if statements
that break out of the loop.
3. Make sure the loop is short to keep the control logic as self-evident as possible.
(Notice this applies to all loop statements,but more so for a loop-and-a-half.)
In this textbook, we will be using loop-and-a-half statements whenever ap-
propriate, that is, whenever it makes the code more readable and clearer. Before we
conclude this section, here’s another loop-and-a-half statement. The loop evaluates
the average score, and it terminates when the input is a negative number.
int cnt = 0;
double score, sum = 0.0;
while (true) {
System.out.print(Enter score: );
score = scanner.nextDouble();
wu23399_ch06.qxd 12/14/06 17:53 Page 317
if (score  0) break;
sum += score;
cnt++;
}
if (cnt  0) {
avg = sum / cnt;
} else {
//error: no input
}
Again, we will use the gcd method as the last example. Here’s the gcd method
using the loop-and-a-half repetition control (we’ll call this version gcd_LaH):
public int gcd_LaH(int m, int n) {
//it doesn't matter which of n and m is bigger
//this method will work fine either way
//assume m,n = 1
int r;
while (true) {
r = n % m;
if (r == 0) break;
n = m;
m = r;
}
return m;
}
318 Chapter 6 Repetition Statements
1. Translate the following while loop to a loop-and-a-half format.
int sum = 0, num = 1;
while (num = 50) {
sum += num;
num++;
}
2. Translate the following do–while loop to a loop-and-a-half format.
int sum = 0, num = 1;
do {
sum += num;
num++;
} while (sum = 5000);
wu23399_ch06.qxd 12/14/06 17:53 Page 318
6.5 The for Statement 319
control variable
; ; ) {
i = 100
for (
sum += i;
}
i = 1 i++
Initialization Update
Boolean Expression
Statement
(loop body)
Figure 6.6 Correspondence of the example for statement to the general format.
6.5 The for Statement
The for statement is the third repetition control statement and is especially suitable
for count-controlled loops. Let’s begin with an example. The following code com-
putes the sum of the first 100 positive integers:
int i, sum = 0;
for (i = 1; i = 100; i++) {
sum += i; //equivalent to sum = sum + i;
}
The general format of the for statement is
for ( initialization; boolean expression; update )
statement
Figure 6.6 shows the correspondence of the sample code above to the general
format. The diagram in Figure 6.7 shows how this statement is executed. The vari-
able i in the statement is called a control variable, and it keeps track of the number
true
statement
boolean expression
initialization
(loop body)
increment
false
i = 100?
i = 1;
sum += i;
i++;
Figure 6.7 A diagram showing the control flow of the example for statement.
wu23399_ch06.qxd 12/14/06 17:53 Page 319
of repetitions. In the sample code, the control variable i is first initialized to 0, and
immediately the boolean expression is evaluated. If the evaluation results in
true, the loop body is executed. Otherwise, the execution of the for statement is
terminated, and the control flows to the statement following this for statement.
Every time the loop body is executed, the increment operator (i++) is executed and
then the boolean expression is evaluated.
The initialization component also can include a declaration of the control
variable. We can do something like this
for (int i = 1; i = 100; i++)
instead of
int i;
for (i = 0; i  10; i++)
The control variable may be initialized to any value, although it is almost always 0 or 1.
The update expression in the example increments the control variable by 1.
We can increment it with values other than 1, including negative values, for example,
for (int i = 0; i  100; i += 5) //i = 0, 5, 10, . .. , 95
for (int j = 2; j  40; j *= 2)//j = 2, 4, 8, 16, 32
for (int k = 100; k  0; k--) //k = 100, 99, 98, 97, ..., 1
Notice that the control variable appears in all three components: initialization,
conditional expression, and update. A control variable does not have to appear
in all three components, but this is the most common style. Many other variations
are allowed for these three components, but for novices, it is safer to use this style
exclusively.
Let’s look at an example from physics. When an object is dropped from height
H, the position P of the object at time t can be determined by the formula
P  16t2
 H
For example, if a watermelon is dropped from the roof of a 256-ft-high dormitory,
it will drop like this:
256 ft at t  0
240 ft at t  1
192 ft at t  2
112 ft at t  3
0 ft at t  4
320 Chapter 6 Repetition Statements
wu23399_ch06.qxd 12/14/06 17:53 Page 320
We can use a for statement to compute the position P at time t. We will input
the initial height and compute the position every second. We repeat this computa-
tion until the watermelon touches the ground. The time the watermelon touches the
ground is derived by solving for t when P  0.
0  16t2
 H
t  
1
H
6


6.5 The for Statement 321
/*
Chapter 6 Sample Program: Dropping a Watermelon
File: Ch6DroppingWaterMelon.java
*/
import java.util.*;
class Ch6DroppingWaterMelon {
public static void main( String[] args ) {
double initialHeight,
position,
touchTime;
Scanner scanner = new Scanner(System.in);
System.out.print(Initial Height:);
initialHeight = scanner.nextDouble();
touchTime = Math.sqrt(initialHeight / 16.0);
touchTime = Math.round(touchTime * 10000.0) / 10000.0;
//convert to four decimal places
System.out.println(nn Time t Position at Time t n);
for (int time = 0; time  touchTime; time++) {
position = -16.0 * time*time + initialHeight;
System.out.print(  + time);
System.out.println(  + position);
}
//print the last second
System.out.println(  + touchTime +  0.00);
}
}
wu23399_ch06.qxd 12/14/06 17:53 Page 321
322 Chapter 6 Repetition Statements
Figure 6.8 The positions of a watermelon dropped from a height of 500 ft.
Java 5.0 introduces a new form of the for statement.There is no formal name for
the newest for loop, but the name for-each loop is used most often. The for-each
loop is a very convenient way to iterate over a collection of items. We will
introduce the new for loop in Chapter 10 and see its use in the data structure
chapters.
The format for the for loop presented in this section is the most basic version.
The Java language allows more complex for statements. For instance, the
initialization and update parts of the for statement are not limited to a single
statement. They can contain zero or more statements. The following two state-
ments,for example,are both valid.
int val, i, j;
for (i = 0, j = 100, val = 0; //init
i  100  j  50; //bool expr
i++, j--) { //increment
val += i - j;
}
System.out.println(val =  + val);
Scanner scanner = new Scanner(System.in);
Running the program with the input value 500.0 for the initial height and using
System.out as output will result in the window shown in Figure 6.8.
wu23399_ch06.qxd 12/14/06 17:53 Page 322
6.5 The for Statement 323
We have introduced three forms of repetition statements—while, do–while, and
for. They are equivalent in their expressive power.In other words,a loop written in
one form of repetition statement can be written by using the other two forms of
repetition statement.Although they are equivalent,in many cases one form would
express the repetition control in a more natural and direct manner. It is your
responsibility as a programmer to implement the repetition control using the
most appropriate form.
int sum, cnt, n;
for (sum = 0, cnt = 0; //init
cnt  10; //bool expr
//increment
System.out.print(Enter number: ),
n = scanner.nextInt(),
sum += n,
cnt++ ) {
}
Do you ever need to write such intricate for statements? Most likely, no.The two
sample statements can be written more clearly and logically in other ways. We
strongly recommend that you stick to the basic, and most logical, form of the for
statement.
1. Write a for loop to compute the following.
a. Sum of 1, 2, . . . , 100
b. Sum of 2, 4, . . . , 500
c. Product of 5, 10, . . . , 50
2. Rewrite the following while loops as for statements.
a. int count = 0, sum = 0;
while ( count  10 ) {
sum += count;
count++;
}
b. int count = 1, sum = 0;
while ( count = 30 ) {
sum += count;
count += 3;
}
wu23399_ch06.qxd 12/14/06 17:53 Page 323
inner
for
outer
for
6.6 Nested for Statements
In many processing tasks, we need to place a for statement inside another for state-
ment. In this section, we introduce a simple nested for statement. We will see more
examples of nested for statements later in the book, especially in Chapter 10 on
array processing.
Suppose we want to display a quick reference table for clerks at the Rugs-R-
Us carpet store. The table in Figure 6.9 lists the prices of carpets ranging in size
from 11  5 ft to 20  25 ft (using System.out for output). The width of a carpet
ranges from 11 to 20 ft with an increment of 1 ft. The length of a carpet ranges
from 5 to 25 ft with an increment of 5 ft. The unit price of a carpet is $19 per
square foot.
We use a nested for statement to print out the table. Let’s concentrate first on
printing out prices. We’ll worry about printing out length and width values later.
The following nested for statement will print out the prices:
int price;
for (int width = 11; width = 20; width++) {
for (int length = 5; length = 25; length += 5) {
price = width * length * 19; //$19 per sq ft.
System.out.print(  + price);
}
//finished one row; now move on to the next row
System.out.println();
}
324 Chapter 6 Repetition Statements
Figure 6.9 The price table for carpets ranging in size from 11  5 ft to 20  25 ft whose unit price is $19 per
square foot.
Length
Width
wu23399_ch06.qxd 12/14/06 17:53 Page 324
Added
statements
The outer for statement is set to range from the first row (width = 11) to the
last row (width = 20). For each repetition of the outer for, the inner for statement is
executed, which ranges from the first column (length = 5) to the fifth column
(length = 25). The loop body of the inner for computes the price of a single carpet
size and prints out this price. So the complete execution of the inner for, which
causes its loop body to be executed 5 times, completes the output of one row. The
following shows the sequence of values for the two control variables.
width length
11
5
10
15
20
25
12
5
10
15
20
25
13
5
10
.
.
.
Now let’s add the code to print out the row and column index values for width
and length.
int price;
System.out.print( 5 10 15 20 25);
System.out.print(nn);
for (int width = 11; width = 20; width++) {
System.out.print(width +  );
for (int length = 5; length = 25; length += 5) {
price = width * length * 19; //$19 per sq ft.
System.out.print(  + price);
}
//finished one row; now move on to the next row
System.out.print(n);
}
6.6 Nested for Statements 325
Completes the printing
of the first row
Completes the printing
of the second row
wu23399_ch06.qxd 12/14/06 17:53 Page 325
The next improvement is to include the labels Width and Length in the output.
This enhancement is left as Exercise 19 at the end of the chapter. Also, in the ex-
ample, literal constants are used for the carpet sizes and the increment value on
length (11, 20, 5, 25, and 5), but in a real program, named constants should be used.
326 Chapter 6 Repetition Statements
1. What will be the value of sum after the following nested for loops are
executed?
a. int sum = 0;
for (int i = 0; i  5; i++) {
sum = sum + i;
for (int j = 0; j  5; j++) {
sum = sum + j;
}
}
b. int sum = 0;
for (int i = 0; i  5; i++) {
sum = sum + i;
for (int j = i; j  5; j++) {
sum = sum + j;
}
}
2. What is wrong with the following nested for loop?
int sum = 0;
for (int i = 0; i  5; i++) {
sum = sum + i;
for (int i = 5; i  0; i--) {
sum = sum + j;
}
}
6.7 Formatting Output
In the table shown in Figure 6.10, the values are aligned very nicely. We purposely
selected the unit price and the ranges of width and length so that the table output
would look good. Notice that the output values are all four-digit numbers. Realisti-
cally, we cannot expect output values to be so uniform. Let’s change the unit price
to $15 and the range of widths to 5 through 14 ft and see what happens. The result
is shown in Figure 6.10, which is not as neat as the previous output. What we need
is a way to format the output so the values are printed out with the proper alignment.
In the code, we used the fixed number of spaces between the values, and it
worked because the output values have the same number of digits. To align the val-
ues with a varying number of digits, we must vary the number of spaces in front of
the values, as shown in Figure 6.11.
The basic idea of formatted output is to allocate the same amount of space for
the output values and align the values within the allocated space. We call the space
wu23399_ch06.qxd 12/14/06 17:53 Page 326
occupied by an output value the field and the number of characters allocated to a
field its field width. In Figure 6.11, the field width is 6.
We have already used two formatting classes—DecimalFormat and Simple-
DateFormat—introduced in Chapters 2 and 3. The most recent version of Java SDK
1.5 has a new general-purpose formatting class called Formatter that includes the
functionalities of DecimalFormat and SimpleDateFormat. For its power, using the
Formatter class is slightly more complicated than using the DecimalFormat and
SimpleDateFormat classes.
To format output using Formatter, first we create its instance by passing the
destination of the output as an argument. Suppose we want to send the formatted
output to System.out; then we create a Formatter object as follows:
Formatter formatter = new Formatter(System.out);
Next we call its format method to output the formatted values. For example, to out-
put an integer with the field width of 6, we write
int num = 467;
formatter.format(%6d, num);
6.7 Formatting Output 327
Figure 6.10 The price table for carpets with $15 per square foot and width ranging from 5 through 14 ft.
Figure 6.11 How to place a varying number of spaces to align the output values.Hyphen is used here to
indicate the blank space.
field
– – – ––3
– – – 445
Each value occupies six spaces. If
the value has three digits, we put
three blank spaces in front. If the
value has four digits, we put two
blank spaces in front, and so forth.
– – – –34
– – – 339
– – 5684
– – –234
– – – –98
– – – 453
– – –231
– – 3444
wu23399_ch06.qxd 12/14/06 17:53 Page 327
The string %6d is called a control string, and it directs how the formatting will take
place. The value 6 specifies the field width, and the control character d indicates the
output value is a decimal integer.
The general syntax for the format method is as follows:
format(control string, expr1, expr2, ...)
The first argument is the control string, and it is followed by zero or more expres-
sions. The control string may include the actual output string in addition to control
values. For example, the statement
int num1, num2, num3;
num1 = 34;
num2 = 9;
num3 = num1 + num2;
formatter.format(%3d + %3d = %5d, num1, num2, num3);
will output
34 + 9 = 43
Figure 6.12 shows how the control values are matched left to right against the
arguments. The figure also illustrates how the noncontrol values (such as + and =
symbols) are output to the destination.
We can change the default left-to-right argument matching by including the
argument index in the control string. The arguments can be indexed as 1$, 2$, and
so forth. For example, the output of
formatter.format(%3$3d is the sum of %2$3d and %1$3d,
num1, num2, num3);
will be
43 is the sum of 9 and 34
328 Chapter 6 Repetition Statements
control string
Figure 6.12 The control values are matched left to right.
3
formatter.format (%3d + %3d = %5d, num1, num2, num3);
4 + 9 = 4 3
wu23399_ch06.qxd 12/14/06 17:53 Page 328
To format real numbers, we include the number of decimal places along with
the field width in the following format:
%field width . decimal places f
The control letter f designates formatting a floating-point number. Here’s an exam-
ple to format 345.9867 using a field of width 15 and two decimal places:
formatter.format(%15.3f, 345.9867);
To format a string, we use the control letter s. Here’s an example:
String name = John;
formatter.format(Hello, %s. Nice to meet you., name);
The output will be
Hello, John. Nice to meet you.
We can also use the format method to format the date information. We use the
control letter t for formatting an instance of GregorianCalendar or Date. The control
letter t must be followed by another control letter that designates the formatting of
the components of the date information, such as month, day, or year. For example,
if we write
GregorianCalendar day = new GregorianCalendar(1776, 6, 4);
formatter.format(%1$tB %1$te, %1$tY, day);
the output will be
July 4, 1776
The date control letter B designates the full month name, e designates the day in two
digits, and Y designates the year in four digits. For other data control letters, please
consult the documentation. Notice that there is only one output argument, and it is
referred to as 1$ three times in the control string.
The use of the Formatter class gives us the most control over the formatting,
but for common output formatting, we can do it by using the format method of
System.out or the String class instead. (In this section, we presented only a subset of
common formatting.) For example, the following code
System.out.format(%5s is %3d years old, Bill, 20);
is equivalent to
Formatter formatter = new Formatter(System.out);
formatter.format(%5s is %3d years old, Bill, 20);
6.7 Formatting Output 329
wu23399_ch06.qxd 12/14/06 17:53 Page 329
(Note: For those who are familiar with C or C++, there’s a method named printf de-
fined for System.out that works exactly the same as the format method. However,
Java’s printf is similar but not identical to the one in C or C++.)
Instead of printing out, it is possible to create a formatted string and assign it
to a variable with the format method of the String class. Here’s an example:
String outputStr
= String.format(%3d + %3d = %5d, num1, num2, num3);
We close the section with a program that produces the carpet price table with
proper alignment for the range of values used in producing the table in Figure 6.10.
Running this program will produce the table shown in Figure 6.13.
330 Chapter 6 Repetition Statements
Figure 6.13 Carpet price table of Figure 6.11 with proper alignment.
/*
Chapter 6 Sample Program: Sample formatting statements
File: Ch6CarpetPriceTableWithFormat.java
*/
class Ch6CarpetPriceTableWithFormat {
public static void main (String[] args) {
int price;
//print out the column labels
System.out.print( ); //put three blank spaces first
for (int colLabel = 5; colLabel =25; colLabel += 5) {
System.out.format(%8d, colLabel);
}
wu23399_ch06.qxd 12/14/06 17:53 Page 330
6.8 Loan Tables 331
System.out.println();
System.out.println();
//print out rows of prices
for (int width = 5; width = 14; width++) {
System.out.format(%3d, width);
for (int length = 5; length = 25; length += 5) {
price = width * length * 15;
System.out.format(%8d, price);
}
//finished one row; now move on to the next row
System.out.println();
}
System.out.println();
System.out.println();
}
}
1. Determine the output of the following code.
System.out.format(%3d + %3d = %3d, 1, 2, 3);
System.out.format(%tY, new Date());
System.out.format(%2$s,%1$s, John, Smith);
2. What’s wrong with the following code?
Formatter f = new Formatter( );
f.format(%8.3f, 232.563);
6.8 Loan Tables
The LoanCalculator program computed the monthly and total payments for a given
loan amount, annual interest rate, and loan period. To see the monthly payment for
the same loan amount and loan period but with a different interest rate, we need to
repeat the calculation, entering the three values again. To illustrate the use of the
concepts introduced in this chapter, let’s design a program that generates a loan
table (similar to the carpet price table) for a given loan amount so we can compare
different monthly payments easily and quickly. The columns of the table are the
loan periods in number of years (5, 10, 15, 20, 25, 30), and the rows are interest
rates ranging from 6 to 10 percent in increments of 0.25.
wu23399_ch06.qxd 12/14/06 17:53 Page 331
In this section, we provide a discussion of the relevant methods only. Let’s
begin with a design of the topmost start method of the top-level controller class.
The start method can be expressed as
tell the user what the program does;
prompt the user Do you want to generate a loan table?;
while (the user says YES) {
input the loan amount;
generate the loan table;
prompt the user Do you want another loan table?;
}
The start method is expressed in pseudocode. Pseudocode is an informal language
we often use to express an algorithm. Pseudocode is useful in expressing an algo-
rithm without being tied down to the rigid syntactic rules of a programming lan-
guage. We can express a simple algorithm in the actual programming language
statements, but for a more complex algorithm, especially those involving nonse-
quential control flow logic, pseudocode is very helpful in expressing the algorithm
concisely and clearly. Whenever appropriate, we will use pseudocode to express
more complex algorithms in the remainder of the book.
Translating the pseudocode into Java code will result in
private static enum Response {YES, NO}
public void start( ) {
Response response;
describeProgram();
response = prompt(Generate a loan table?);
while (response == Response.YES) {
loanAmount = getLoanAmount(); //get input
generateLoanTable(loanAmount); //generate table
response = prompt(Generate another loan table?);
}
}
private Response prompt(String question) {
String input;
Response response = Response.NO;
System.out.print(question +  (Yes - y; No - n): );
input = scanner.next();
332 Chapter 6 Repetition Statements
pseudocode
scanner is created in a
constructor
wu23399_ch06.qxd 12/14/06 17:53 Page 332
if (input.equals(Y) || input.equals(y)) {
response = Response.YES;
}
return response;
}
Notice how the actual start method is almost as easy to read as the pseudocode. By
using objects and well-designed (sub)methods, we can express methods that are as
easy to read as pseudocode.
The describeProgram method tells the user what the program does if the user
requests it. The getLoanAmount method gets the loan amount from the user. The
method will allow the user to enter the loan amount between 100.0 and 500000.0. The
generateLoanTable method generates the loan table, which we explain in detail next.
We use a nested loop to generate the table. Both the inner and outer loops are
count-controlled loops. The loop for columns (years) will range from 5 to 30 with
an increment of 5 and the loop for rows (rates) will range from 6.0 to 10.0 with an
increment of 0.25. So the nested loop can be written as follows:
private static final int BEGIN_YEAR = 5;
private static final int END_YEAR = 30;
private static final int YEAR_INCR = 5;
private static final double BEGIN_RATE = 6.0;
private static final double END_RATE = 10.0;
private static final double RATE_INCR = 0.25;
...
for (double rate = BEGIN_RATE; rate = END_RATE;
rate += RATE_INCR){
for (int year = BEGIN_YEAR; year = END_YEAR;
year += YEAR_INCR){
...
//compute and display the monthly loan payment
//for a given year and rate
}
}
Notice the outer loop is using double as the loop counter, something we dis-
couraged in Section 6.2. In this particular case, with the increment value of 0.25,
there will be no problem because this value can be represented precisely in com-
puter memory. Moreover, the terminating condition rate = END_RATE guarantees
that the loop will terminate eventually if we keep adding RATE_INCR to rate.
To compute the monthly loan payment, we simply reuse the Loan class we
defined in Chapter 4 as follows:
double amount = ... ;
double rate = ... ;
int period = ... ;
6.8 Loan Tables 333
wu23399_ch06.qxd 12/14/06 17:53 Page 333
Loan loan = new Loan( );
double monthlyPayment
= loan.getMonthlyPayment(amount, rate, period);
This is the power of object-oriented programming. Because a single well-
defined task of loan computation and nothing else is coded in the Loan class, we are
able to reuse it here easily. What would happen had we not designed the Loan class?
It was certainly possible for us to complete the Chapter 4 sample development pro-
gram with one service class that handles everything: input, output, and computation
tasks. The chance of reusing such a class, however, is very low. Just as we do not
expect to buy a textbook that teaches all five subject matters of single-variable cal-
culus, introduction to economics, organic chemistry, introduction to programming,
and western civilization, we do not want a service class that is overloaded with
many different types of tasks. We do not want one class that does everything.
Rather, we want many classes, with each class doing one task effectively and effi-
ciently. This will allow us to mix and match the classes easily.
Finally, the output values can be formatted by using the technique introduced
in Section 6.7. Overall design is now complete. It is left as an exercise (Exercise 17)
to implement the loan table calculation program.
6.9 Estimating the Execution Time
We promised at the end of Section 6.1 to compare the two versions of gcd methods
experimentally. Detailed analysis of algorithms is beyond the scope of this book
(we provide a little bit of analytical comparisons of sorting algorithms in this book),
but experimental analysis is within our realm. We can compare the performance of
different methods by actually running them and clocking their execution times.
Here’s the basic idea:
Start the clock (stopwatch)
Run the method
Stop the clock
Report the elapsed time
There is no clock or stopwatch standard class, but we can time the execution
by using the Date class from the java.util package. Before we call the method we
want to time, we record the start time by creating a Date object. After the method is
completed, we record the end time by creating a second Date object. Calling the
getTime method of the Date class returns the number of milliseconds (1 ms 
1/1000 s) since January 1, 1970 00:00:00 Greenwich Mean Time. So by subtracting
the start time from the end time, we can get the elapsed time in milliseconds. Here’s
the general idea:
Date startTime = new Date();
//the method call comes here
334 Chapter 6 Repetition Statements
wu23399_ch06.qxd 12/14/06 17:53 Page 334
Date endTime = new Date();
long elapsedTimeInMilliseconds =
endTime.getTime() - startTime.getTime();
Now let’s write a short program to time the performance of gcd and
gcd_bruteforce. The program includes many of the techniques discussed in this
chapter. Here’s the program (we do not repeat the method bodies of gcd and
gcd_bruteforce here):
6.9 Estimating the Execution Time 335
/*
Chapter 6 Sample Program: Time the performance of gcd methods
File: Ch6TimeGcd.java
*/
import java.util.*;
class Ch6TimeGcd {
private static enum ComputationType {BRUTE_FORCE, EUCLID}
private Scanner scanner;
public static void main(String[] args) {
Ch6TimeGcd tester = new Ch6TimeGcd( );
tester.start();
System.exit(0);
}
public Ch6TimeGcd() {
scanner = new Scanner(System.in);
}
public void start( ) {
long bruteForceTime, euclidTime;
int m, n;
while (isContinue()) {
m = getPositiveInteger( );
n = getPositiveInteger( );
//Time the brute force method
bruteForceTime = timeMethod(m, n, ComputationType.BRUTE_FORCE);
//Time the Euclidean method
euclidTime = timeMethod(m, n, ComputationType.EUCLID);
wu23399_ch06.qxd 12/14/06 17:53 Page 335
336 Chapter 6 Repetition Statements
System.out.println(M:  + m);
System.out.println(N:  + n);
System.out.println(Brute Force Time:  + bruteForceTime);
System.out.println(Euclidean Time:  + euclidTime + n);
}
}
private long timeMethod(int m, int n, ComputationType type) {
Date startTime, endTime;
startTime = new Date();
if (type == ComputationType.BRUTE_FORCE) {
gcd_bruteforce(m, n);
} else {
gcd(m, n);
}
endTime = new Date();
return (endTime.getTime() - startTime.getTime());
}
private int getPositiveInteger( ) {
int input;
while (true) {
System.out.print(Enter positive integer (0 is okay):);
input = scanner.nextInt();
if (input = 0) break;
System.out.println(Input must be 0 or more);
}
return input;
}
private boolean isContinue( ) {
String input;
boolean response = false;
System.out.print(Run test? );
input = scanner.next();
if (input.equals(Y) || input.equals(y)) {
response = true;
}
wu23399_ch06.qxd 12/14/06 17:53 Page 336
return response;
}
private int gcd_bruteforce(int m, int n) {
. . .
}
private int gcd(int m, int n) {
. . .
}
}
6.9 Estimating the Execution Time 337
Here’s a sample interaction:
Run test? y
Enter positive integer (0 is okay):4567820
Enter positive integer (0 is okay):2147483640
M: 4567820
N: 2147483640
Brute Force Time: 94
Euclidean Time: 0
Run test? y
Enter positive integer (0 is okay):1457689098
Enter positive integer (0 is okay):2147483640
M: 1457689098
N: 2147483640
Brute Force Time: 31953
Euclidean Time: 0
Run test? n
The value of 0 for Euclidean time does not imply that it took no time to com-
pute the result. It means that the time it took was so miniscule, we weren’t able to
detect it by the technique we used. Notice that, for the brute-force approach, the dif-
ference in the running times between the small and large values for M is substantial,
while the difference for the Euclidean approach is not discernible. Detailed analysis
will actually tell us that the running time for the brute-force approach is linearly
proportional to the input size M, while the Euclidean approach is logarithmically
proportional to the input size M. So, for the second comparison in the sample run,
there will be 1,457,689,098 divisions performed (actually twice this number
because we are executing m % i == 0  n % i == 0) in gcd_bruteforce, but only log
1,457,689,098  9 divisions. See how superior the Euclidean approach is?
wu23399_ch06.qxd 12/14/06 17:53 Page 337
Keep in mind that the value we get for the elapsed time is a rough estimate. For
one thing, the values we get for the elapsed time differ dramatically according to
which CPU we run the program on and whether other processes are running at the
same time (e.g., if a garbage collection routine kicks in while a method is running,
then the runtime estimate can be way off). Also, the granularity is very coarse when
timed from a high-level language such as Java. For example, it is not possible to dis-
tinguish between the program that runs in 5 ms and the program that runs in 6 ms.
Although the value is a rough estimate, it still give us useful information such as the
rate of increase in execution time as we increase the size of input values.
338 Chapter 6 Repetition Statements
To estimate the running time of a loop statement:
1. Record the start time by creating a Date object,say, startTime, before the loop
statement.
2. Record the end time by creating another Date object,say,endTime, after the
loop statement.
3. Elapsed time (in milliseconds) is computed as follows:
elapsedTime = endTime.getTime()
– startTime.getTime();
6.10 Recursive Methods (Optional)
In addition to the three repetition control statements we introduced in this chapter,
there is a fourth way to control the repetition flow of a program by using recursive
methods. A recursive method is a method that contains a statement (or statements)
that makes a call to itself. We explain recursive methods briefly in this section.
Realistic examples of recursive methods will be given in Chapter 15.
So far, we have seen only methods that call other methods, something like this:
methodOne(...) {
...
methodTwo(...); //methodOne called methodTwo
...
}
methodTwo(...) {
...
}
A recursive method calls itself, and it looks something like this:
methodOne(...) {
...
recursive
method
wu23399_ch06.qxd 12/14/06 17:53 Page 338
methodOne (...); //calls the method itself
...
}
At first glance, it seems as if a recursive call will never end since the call is made to
the same method. Indeed, if you do not follow the rules, you could end up with
infinite recursive calls. In this section we explain how to write recursive methods
correctly.
Suppose we want to compute the factorial of N. The factorial of N is the prod-
uct of the first N positive integers, denoted mathematically as
N! = N * (N-1) * (N-2) * ... * 2 * 1
We will write a recursive method to compute the factorial of N. Mathemati-
cally, we can define the factorial of N recursively as
6.10 Recursive Methods (Optional) 339
factorial(N)=

1 if N = 1
N * factorial (N-1) otherwise
The definition states that if N is 1, then the function factorial(N) has the
value 1. Otherwise, the function factorial(N) is the product of N and factorial(N –1).
For example, the function factorial(4) is evaluated as follows:
The recursive factorial method parallels the preceding mathematical defini-
tion. The method is defined thus:
//Assume N is greater than 0
public int factorial(int N) {
if (N == 1)
return 1;
else
return N * factorial(N-1);
}
factorial(4)
4 * factorial(3)
24
6
2
1
3 * factorial(2)
2 * factorial(1)
1
Recursive case:
recursion continues with
another recursive call.
Test to stop or continue.
End case: recursion stops.
wu23399_ch06.qxd 12/14/06 17:53 Page 339
The diagram in Figure 6.14 illustrates the sequence of calls for the recursive
factorial method. Recursive methods will contain three necessary components.
340 Chapter 6 Repetition Statements
The three necessary components in a recursive method are
1. A test to stop or continue the recursion.
2. An end case that terminates the recursion.
3. A recursive call(s) that continues the recursion.
To ensure that the recursion will stop eventually, we must pass arguments different
from the incoming parameters. In the factorial method, the incoming parameter was
N, while the argument passed in the recursive call was N1. This difference of 1 be-
tween the incoming parameter and the argument will eventually make the argument
in a recursive call be 1, and the recursion will stop.
Let’s implement two more mathematical functions using recursion. The next
method computes the sum of the first N positive integers 1, 2, . . ., N. Notice how this
method includes the three necessary components of a recursive method.
public int sum ( int N ) { //assume N = 1
if (N == 1)
return 1;
else
return N + sum( N-1 );
}
The last method computes the exponentiation AN
, where A is a real number
and N is a positive integer. This time, we have to pass two arguments—A and N.
The value of A will not change in the calls, but the value of N is decremented after
each recursive call.
public double exponent ( double A, int N ) {
if (N == 1)
return A;
else
return A * exponent( A, N-1 );
}
So far we used only mathematical functions to illustrate recursive methods,
but recursion is not limited to mathematical functions. Let’s look at one example.
We know the length method of the String class returns the number characters in a
given string. Let’s write a recursive method that does the same thing. Here’s how we
think recursively. The total number of characters in a string is 1 plus the number of
characters in the substring from the second position to the end of the string. If the
wu23399_ch06.qxd 12/14/06 17:53 Page 340
6.10 Recursive Methods (Optional) 341
int factorial(int N){
if (N==1)
return 1;
else
return N * factorial(N–1);
}
N4
24
int factorial(int N){
if (N==1)
return 1;
else
return N * factorial(N–1);
}
N3
6
int factorial(int N){
if (N==1)
return 1;
else
return N * factorial(N–1);
}
N2
2
int factorial(int N){
if (N==1)
return 1;
else
return N * factorial(N–1);
}
N1
1
Figure 6.14 The sequence of calls for the recursive factorial method.
wu23399_ch06.qxd 12/14/06 17:53 Page 341
string has no characters, then the length is zero. Puting this idea into an actual
method, we have
public int length(String str) {
if (str.equals()) { //str has no characters
return 0;
} else {
return 1 + length(str.substring(1));
}
}
We will present more examples of recursive methods that implement nonnu-
merical operations in Chapter 15.
We used factorial, sum, exponentiation, and length as examples to introduce
some of the basic concepts of recursion, but we should never actually write these
methods using recursion. The methods can be written more efficiently in an itera-
tive (i.e., nonrecursive) manner using a simple for loop. In practice, we use recur-
sion if certain conditions are met.
342 Chapter 6 Repetition Statements
Index of the second
position is 1.
Use recursion if
1. A recursive solution is natural and easy to understand.
2. A recursive solution does not result in excessive duplicate computation.
3. The equivalent iterative solution is too complex.
As a final review of the topic, we conclude this section with the recursive
version of the Euclidean gcd method. Remember the logic behind the Euclidean
gcd method is a sequence of reducing the problem, for example, gcd(48, 16) =
gcd(16, 12) = gcd(12, 4) = 4. Here’s how we can express this thinking recursively:
public int gcd_recursive(int m, int n) {
int result;
if (m == 0) { //test
result = n; //end case
} else {
result = gcd_recursive(n % m, m); //recursive case
}
return result;
}
wu23399_ch06.qxd 12/14/06 17:53 Page 342
6.11 Sample Development 343
Sample Development
6.11 Sample Development
Hi-Lo Game
In this section we will develop a program that plays a Hi-Lo game.This program illustrates
the use of repetition control,the random number generator,and the testing strategy.The
objective of the game is to guess a secret number between 1 and 100.The program will
respond with HI if the guess is higher than the secret number and LO if the guess is lower
than the secret number.The maximum number of guesses allowed is six.If we allow up to
seven,one can always guess the secret number.Do you know why?
Problem Statement
Write an application that will play Hi-Lo games with the user.The objective of the
game is for the user to guess the computer-generated secret number in the least
number of tries. The secret number is an integer between 1 and 100, inclusive.
When the user makes a guess, the program replies with HI or LO depending on
whethertheguessishigherorlowerthanthesecretnumber.Themaximumnum-
ber of tries allowed for each game is six.The user can play as many games as she
wants.
Overall Plan
We will begin with our overall plan for the development. Let’s identify the major tasks of
the program.The first task is to generate a secret number every time the game is played,
and the second task is to play the game itself.We also need to add a loop to repeat these
two tasks every time the user wants to play the Hi-Lo game.We can express this program
logic in pseudocode as follows:
do {
Task 1: generate a secret number;
Task 2: play one game;
} while ( the user wants to play );
Let’s look at the two tasks and determine objects that will be responsible for han-
dling the tasks.For the first task,we will use the random method of the Math class.We will
examine this method in detail later to determine whether it is the one we can use in the
program. If this method does not meet our needs, then we will explore further and most
likely will have to derive our own random number generator.
For the second task of playing the game itself, we use objects that handle I/O and
the logic of repeatedly asking for the next guess until the game is over.For input and out-
put,we use a Scanner and System.out. We will define a class to handle the logic of play-
ing the game. This class will control the other two classes. We will name this class
Ch6HiLo, and it will be an instantiable main class.
program
tasks
wu23399_ch06.qxd 12/14/06 17:53 Page 343
344 Chapter 6 Repetition Statements
6.11 Sample Development—continued
Here’s our working design document:
Design Document: Ch6HiLo
Class Purpose
Ch6HiLo The top-level control object handles the logic of
playing games and manages other objects.This is
the instantiable main class.
Scanner This standard class is for inputting user guesses.
PrintStream This standard class is for displaying hints and other
(System.out) messages.
Figure 6.15 is the program diagram for this program. A keen observer may have noticed
that the Ch6HiLo class is handling both types of tasks:handling of user interface and con-
trolling the logic of game playing. We will revisit this design in the GUI chapter and
provide an alternative.The one-class design we adopt here may not be an ideal design,
but may be acceptable for a simplistic game such as this one.The design also provides
us with a meaningful comparison when we present an alternative design in the GUI
chapter.
We will implement this program using the following four major steps:
1. Start with a skeleton Ch6HiLo class.
2. Add code to the Ch6HiLo class to play a game using a dummy secret number.
3. Add code to the Ch6HiLo class to generate a random number.
4. Finalize the code by removing temporary statements and tying up loose ends.
Step 1 Development: Program Skeleton
The structure of the HiLoMain class is the same as other main classes.All we need to do is
to declare, create, and start a HiLo object. Instead of forcing the user to play at least one
program
classes
Figure 6.15 The program diagram for the HiLo program.
Ch6HiLo PrintStream
(System.out)
Scanner
develop-
ment steps
step 1
design
wu23399_ch06.qxd 12/14/06 17:53 Page 344
game,we will implement the program so the user has an option of not playing a game at
all.In pseudocode we can express this logic as follows:
describe the game rules;
prompt the user to play a game or not;
while ( answer is yes ) {
generate the secret number;
play one game;
prompt the user to play another game or not;
}
Notice that we use a while loop here, so the user can quit the program without
playing a game. If we use a do–while loop instead, then the user must play at least one
game before stopping the program.We opt to use the while loop because the user may
not want to play the game at all after reading the game rules.
We use a private method describeRules to display the game rules.Another private
method named prompt gets a yes/no reply from the user.We call this method to ask if the
user wants to play a game.To generate a secret number,we have the third private method
generateSecretNumber. Lastly, we define the fourth private method playGame to play
one game.We declare these four methods private because these methods are for inter-
nal use. As always, we will use the constructor to perform necessary object creation and
initialization.
Our working design document for the HiLo class is as follows:
6.11 Sample Development 345
Design Document:The Ch6HiLo Class
Method Visibility Purpose
constructor public Creates and initializes the objects
used by a HiLo object.
start public Starts the Hi-Lo game playing.The
user has an option of playing a game
or not.
describeRules private Displays the game rules in
System.out.
generateSecretNumber private Generates a secret number for the
next Hi-Lo game.
playGame private Plays one Hi-Lo game.
prompt private Prompts the user for a yes/no reply.
wu23399_ch06.qxd 12/14/06 17:53 Page 345
6.11 Sample Development—continued
346 Chapter 6 Repetition Statements
For the skeleton program, we include temporary output statements in the private
methods to verify that they are called correctly in the right order. Here’s the skeleton
Ch6HiLo class:
step 1 code
import java.util.*;
/*
Chapter 6 Sample Development: Hi-Lo Game (Step 1)
The instantiable main class of the program.
*/
class Ch6HiLo {
private static enum Response {YES, NO}
private Scanner scanner;
//Main Method
public static void main (String[] args) {
Ch6HiLo hiLo = new Ch6HiLo( );
hiLo.start();
}
public Ch6HiLo( ) {
scanner = new Scanner(System.in);
}
public void start( ) {
Response answer;
describeRules();
answer = prompt(Do you want to play a Hi-Lo game?);
while (answer == Response.YES) {
generateSecretNumber( );
playGame();
answer = prompt(Do you want to play another Hi-Lo game?);
}
System.out.println(Thank you for playing Hi-Lo.);
}
Constructor
start
main
wu23399_ch06.qxd 12/14/06 17:54 Page 346
private void describeRules( ) {
System.out.println(Inside describeRules); //TEMP
}
private void generateSecretNumber( ) {
System.out.println(Inside generateSecretNumber); //TEMP
}
private void playGame( ) {
System.out.println(Inside playGame); //TEMP
}
private Response prompt(String question) {
String input;
Response response = Response.NO;
System.out.print(question +  (Yes - y; No - n): );
input = scanner.next();
if (input.equals(Y) || input.equals(y)) {
response = Response.YES;
}
return response;
}
6.11 Sample Development 347
describeRules
generateSecretNumber
playGame
prompt
We execute the skeleton Ch6HiLo class to verify that the class is coded correctly. To
verify the correct execution of step 1,we attempt to play the game
1. Zero times
2. One time
3. One or more times
For the first run, we select No to the prompt Do you want to play a Hi-Lo game?
and make sure the program stops without playing a game. For the second run, we select
Yes to the first prompt and verify that the messages Inside generateSecretNumber and
Inside playGame are shown in the console window. We select No to the prompt Do you
want to play another Hi-Lo game? and make sure the program stops.For the third run,
we make sure we can play more than one game. After we verify all the scenarios work
correctly,we proceed to the next step.
step 1 test
wu23399_ch06.qxd 12/14/06 17:54 Page 347
6.11 Sample Development—continued
Step 2 Development: Play a Game with a Dummy Secret Number
In the second development step, we add a routine that plays a Hi-Lo game. Let’s begin
with the control flow of the playGame method. There are two cases to end a Hi-Lo
game: The user either guesses the number in less than six tries or uses up all six tries
without guessing the number. So we need a counter to keep track of the number of
guesses made.Let’s call this counter guessCount. We stop the game when guessCount
becomes larger than 6 or the user’s guess is equal to the secret number. At the end
of the game, we output an appropriate message. Expressing this in pseudocode, we
have
//Method: playGame
set guessCount to 0;
do {
get next guess;
increment guessCount;
if (guess  secretNumber) {
print the hint LO;
} else if (guess  secretNumber) {
print the hint HI;
}
} while (guessCount  number of guesses allowed 
guess != secretNumber );
if (guess == secretNumber) {
print the winning message;
} else {
print the losing message;
}
All variables used in this method will be local except secretNumber, which will be an in-
stance variable. The value for secretNumber is set inside the generateSecretNumber
method.
To support a better user interface,we will include an input error handling that allows
the user to enter only values between 1 and 100. We will do this input-error-checking rou-
tine in a new private method getNextGuess because we do want to keep the playGame
method clean and simple. If we included the code for input error handling directly inside
348 Chapter 6 Repetition Statements
step 2
design
wu23399_ch06.qxd 12/14/06 17:54 Page 348
the playGame method, the method would become too cluttered and lose the overall clar-
ity of what the method is doing. Pseudocode for the getNextGuess method is
//Method: getNextGuess
while ( true ) {
get input value;
if (valid input) return input value;
print error message;
}
The working design document of the class now includes this new private method:
6.11 Sample Development 349
step 2 code
Design Document: The Ch6HiLo Class
Method Visibility Purpose
... ... ...
getNextGuess private Returns the next guess from the user.Only
accepts a guess between 1 and 100. Prints
an appropriate error message when an
invalid guess is entered.
In the step 2 coding, we need to implement three methods. In addition to
the playGame and getNextGuess methods, we need to define a temporary gen-
erateSecretNumber method so we can test the playGame method. The temporary
generateSecretNumber method assigns a dummy secret number to the instance vari-
able secretNumber. The temporary method is coded as follows:
private void generateSecretNumber( ) {
secretNumber = 45; //TEMP
}
Any number will do; we simply picked the number 45. Knowing that the secret number is
45, we will be able to test whether the playGame method is implemented correctly.
We implement the playGame method thus:
private void playGame( ) {
int guessCount = 0;
int guess;
do {
//get the next guess
guess = getNextGuess();
guessCount++;
getNextGuess is a
new private method.
wu23399_ch06.qxd 12/14/06 17:54 Page 349
6.11 Sample Development—continued
//check the guess
if (guess  secretNumber) {
System.out.println
Your guess is LO);
} else if (guess  secretNumber) {
System.out.println
Your guess is HI);
}
} while ( guessCount  MAX_GUESS_ALLOWED 
guess != secretNumber );
//output appropriate message
if ( guess == secretNumber ) {
System.out.println
You guessed it in 
+ guessCount +  tries.);
} else {
System.out.println
You lost. Secret No. was 
+ secretNumber);
}
}
The getNextGuess method will accept an integer between 1 and 100. The method
uses a while loop to accomplish this:
private int getNextGuess( ) {
int input;
while (true) {
System.out.print(Next Guess: );
input = scanner.nextInt();
if (LOWER_BOUND = input  input = UPPER_BOUND) {
return input;
}
//invalid input; print error message
System.out.println(Invalid Input:  +
Must be between  + LOWER_BOUND +
and  + UPPER_BOUND);
}
}
350 Chapter 6 Repetition Statements
Repeat the loop if
the number of tries
is not used up and
the correct guess is
not made.
This class constant
is set to 6.
wu23399_ch06.qxd 12/14/06 17:54 Page 350
The necessary constant and instance variable are declared in the data member sec-
tion of the HiLo class as follows:
//---------------------------------
// Data Members
//---------------------------------
private final int MAX_GUESS_ALLOWED = 6;
private final int LOWER_BOUND = 1;
private final int UPPER_BOUND = 100;
private int secretNumber;
We need to test two methods in this step. To verify the getNextGuess method,we
input both invalid and valid guesses. We verify the method by running the following tests:
1. Enter a number less than 1.
2. Enter a number greater than 100.
3. Enter a number between 2 and 99.
4. Enter 1.
5. Enter 100.
The first two test cases are called error cases, the third is called the normal case, and
the last two are called end cases. One of the common errors beginners make is to create a
loop statement that does not process the end cases correctly. When our code handles all
three types of cases correctly, we will proceed to test the playGame method.
To verify the playGame method, we need to perform a more elaborate testing.
Knowing that the dummy secret number is 45, we verify the playGame method by run-
ning the following tests:
1. Enter a number less than 45 and check that the correct hint LO is displayed.
2. Enter a number greater than 45 and check that the correct hint HI is displayed.
3. Enter the correct guess,and check that the game terminates after displaying the
appropriate message.
4. Enter six wrong guesses,and check that the game terminates after displaying the
appropriate message.
When all four tests are successfully completed, we proceed to the next step.
Step 3 Development: Generate a Random Number
In step 3,we add a routine that generates a random number between 1 and 100. As
explained in Chapter 3, we can use the method random from the Math package. Since
6.11 Sample Development 351
step 2 test
test cases
step 3
design
wu23399_ch06.qxd 12/14/06 17:54 Page 351
6.11 Sample Development—continued
the range is between 1 and 100, we can simplify the formula as
secretNumber = X  100  1
where 0.0 ≤ X  1.0.
The generateSecretNumber method is defined thus:
private void generateSecretNumber( ) {
double X = Math.random();
secretNumber = (int) Math.floor( X * 100 ) + 1;
System.out.println(Secret Number:  + secretNumber);
// TEMP
}
The method includes a temporary statement to output the secret number so we can stop
the game anytime we want by entering the correct guess.
To verify that the method generates correct random numbers,we will write a sepa-
rate test program. If we don’t use such a test program and instead include the method
immediately in the Ch6HiLo class, we have to play the game, say, 100 times to verify that
the first 100 generated numbers are valid. The test program generates N random num-
bers and stops whenever an invalid number is generated. We will set N to 1000. Here’s the
test program:
class TestRandom {
public static void main (String[] args) {
int N = 1000, count = 0, number;
double X;
do {
count++;
X = Math.random();
number = (int) Math.floor( X * 100 ) + 1;
} while ( count  N 
1 = number  number = 100 );
if ( number  1 || number  100 ) {
System.out.println(Error:  + number);
} else {
System.out.println(Okay);
}
}
}
352 Chapter 6 Repetition Statements
step 3 code
step 3 test
TestRan-
dom class
for testing
wu23399_ch06.qxd 12/14/06 17:54 Page 352
Keep in mind that successfully generating 1000 valid random numbers does
not guarantee that the 1001st number is also valid. We did not offer any formal
mathematical proof that the routine for the random number generator works correctly.
What we are doing here is making an assumption that no user wants to play more than
1000 Hi-Lo games in one session, which we believe is a practical and reasonable
assumption. After the TestRandom class is executed correctly, we make the necessary
changes to the Ch6HiLo class and run it. When we verify that the program runs as
expected, we proceed to the final step.
Step 4 Development: Finalize
We finalize the program in the last step. We will perform a critical review of the program,
looking for any unfinished method, inconsistency, or error in the methods; unclear or
missing comments; and so forth. We should also not forget to keep an eye on any im-
provement we can make to the existing code.
We still have a temporary code inside the describeRules method, so we will
complete the method by adding code to describe the game rules. This method is left as
Exercise 18.
There are still temporary output statements that we used for verification purposes.
We can either delete them from the program or comment them out. We will leave them
in the program by commenting them out so when the time comes for us to modify,
debug,or update the program,we do not have to reenter them.
Summary 353
program
review
• A repetition control statement is used to repeatedly execute a block of code
until a certain condition is met.
• Three repetition control statements are while, do–while, and for.
• The count-controlled loop executes the loop body for a fixed number of
times.
• The sentinel-controlled loop executes the loop body until any one of the
designated values called a sentinel is encountered.
• Count-controlled loops can be implemented most naturally with the for
statements.
• Sentinel-controlled loops can be implemented most naturally with the while
or do–while statements.
• The while statement is called a pretest loop, and the do–while statement is
called a posttest loop. The for statement is also a pretest loop.
• Reading a value before the loop statement is called a priming read.
• Off-by-1 error and infinite loop are two common mistakes in writing a loop
control.
S u m m a r y
wu23399_ch06.qxd 12/14/06 17:54 Page 353
• The loop-and-a-half repetition control is the most general way of writing a
loop. The break statement is used within the loop body to exit the loop when
a certain condition is met.
• The nested for statement is used very often because it is ideally suited to
process tabular data.
• Output values can be formatted by using the Formatter class.
• Execution time can be estimated by using the Dafe class.
354 Chapter 6 Repetition Statements
repetition statements
while statements
do–while statements
for statements
off-by-1 error
infinite loop
priming read
nested for statements
pseudocode
loop-and-a-half control
one-entry-one-exit control
count-controlled loops
sentinel-controlled loops
pretest and posttest loops
formatting output values
recursive methods (optional)
K e y C o n c e p t s
1. Identify all the errors in the following repetition statements. Some errors are
syntactical while others are logical (e.g., infinite loops).
a. for (int i = 10; i  0; i++) {
x = y;
a = b;
}
b. int sum = 0;
Scanner scanner = new Scanner(System.in);
do {
num = scanner.nextInt();
sum += num;
} until (sum  10000);
c. while (x  1  x  10) {
a = b;
}
d. while (a == b) ;
{
a = b;
x = y;
}
E x e r c i s e s
wu23399_ch06.qxd 12/14/06 17:54 Page 354
e. for (int i = 1.0; i = 2.0; i += 0.1) {
x = y;
a = b;
}
2. Write for, do–while, and while statements to compute the following sums
and products.
a. 1  2  3  . . .  100
b. 5  10  15  . . .  50
c. 1  3  7  15  31  . . .  (220
 1)
d. 1  
1
2
  
1
3
  
1
4
  . . .  
1
1
5

e. 1  2  3  . . .  20
f. 1  2  4  8  . . .  220
3. What will be the value of sum after each of the following nested loops is
executed?
a. sum = 0;
for (int i = 0; i = 10; i++)
for (int j = 0; j = 10; j++)
sum += i ;
b. sum = 0;
j = 0;
do {
j++;
for (int i = 5; i  j; i--)
sum = sum + (i+j);
} while (j  11);
c. sum = 0;
i = 0;
while (i  5) {
j = 5;
while (i != j) {
sum += j;
j--;
}
i++;
}
d. sum = 0;
for (int i = 0; i = 10; i++)
for (int j = 10; j  2*i; j--)
sum = sum + (j - i);
Exercises 355
wu23399_ch06.qxd 12/14/06 17:54 Page 355
4. Determine the output from the following code without actually executing it.
System.out.format(%4d, 234);
System.out.format(%5d, 234);
System.out.format(%s, n);
System.out.format($%6.2f, 23.456);
System.out.format(%s, n);
System.out.format(%1$3d+%1$3d=%2$5d, 5, (5+5));
5. Rewrite the following nested for statements, using nested do–while and
while statements.
a. sum = 0;
number = 0;
for (int i = 0; i = 10; i++)
for (int j = 10; j = i; j--) {
number++;
sum = sum + (j - i);
}
b. product = 1;
number = 0;
for (int i = 1; i  5; i++)
for (int j = 1; j  5; j++) {
number++;
product *= number;
}
6. You can compute sin x and cos x by using the following power series:
sin x  x  
3
x3
!
  
5
x5
!
 
7
x7
!
  . . .
cos x  1 
2
x2
!
  
4
x4
!
  
6
x6
!
  . . .
Write a program that evaluates sin x and cos x by using the power series. Use
the double data type, and increase the number of terms in the series until the
overflow occurs. You can check if the overflow occurs by comparing the
value against Double. POSITIVE_INFINITY. Compare the results you obtain to
the values returned by the sin and cos methods of the Math class.
7. Write an application to print out the numbers 10 through 49 in the following
manner:
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
356 Chapter 6 Repetition Statements
wu23399_ch06.qxd 12/14/06 17:54 Page 356
How would you do it? Here is an example of poorly written code:
for (int i = 10; i  50; i++) {
switch (i) {
case 19:
case 29:
case 39: System.out.println(  + i); //move to the
break; //next line
default: System.out.print(  + i);
}
}
This code is not good because it works only for printing 10 through 49. Try
to develop the code so that it can be extended easily to handle any range of
values. You can do this coding in two ways: with a nested for statement or
with modulo arithmetic. (If you divide a number by 10 and the remainder is
9, then the number is 9, 19, 29, or 39, and so forth.)
8. A prime number is an integer greater than 1 and divisible by only itself and
1. The first seven prime numbers are 2, 3, 5, 7, 11, 13, and 17. Write a
method that returns true if its parameter is a prime number.
9. There are 25 primes between 2 and 100, and there are 1229 primes between
2 and 10,000. Write a program that inputs a positive integer N  2 and
displays the number of primes between 2 and N (inclusive). Use the timing
technique explained in Section 6.9 to show the amount of time it took to
compute the result.
10. Instead of actually computing the number of primes between 2 and N, we
can get an estimate by using the Prime Number Theorem, which states that
prime(N )  
ln
N
(N)

where prime(N) is the number of primes between 2 and N (inclusive). The
function ln is the natural logarithm. Extend the program for Exercise 9 by
printing the estimate along with the actual number. You should notice the
pattern that the estimate approaches the actual number as the value of N gets
larger.
11. A perfect number is a positive integer that is equal to the sum of its proper
divisors. A proper divisor is a positive integer other than the number itself
that divides the number evenly (i.e., no remainder). For example, 6 is a
perfect number because the sum of its proper divisors 1, 2, and 3 is equal
to 6. Eight is not a perfect number because 1  2  4  8 . Write an
application that accepts a positive integer and determines whether the
number is perfect. Also, display all proper divisors of the number. Try a
number between 20 and 30 and another number between 490 and 500.
12. Write an application that lists all perfect numbers between 6 and N, an upper
limit entered by the user. After you verify the program with a small number
Exercises 357
wu23399_ch06.qxd 12/14/06 17:54 Page 357
for N, gradually increase the value for N and see how long the program takes
to generate the perfect numbers. Since there are only a few perfect numbers,
you might want to display the numbers that are not perfect so you can easily
tell that the program is still running.
13. Write a program that displays all integers between low and high that are the
sum of the cube of their digits. In other words, find all numbers xyz such that
xyz  x3
 y3
 z3
, for example, 153  13
 53
 33
. Try 100 for low and
1000 for high.
14. Write a method that returns the number of digits in an integer argument; for
example, 23,498 has five digits.
15. Your freelance work with MyJava Lo-Fat Burgers was a success (see
Exercise 22 of Chap. 5). The management loved your new drive-through
ordering system because the customer had to order an item from each of the
three menu categories. As part of a public relations campaign, however,
management decided to allow a customer to skip a menu category. Modify
the program to handle this option. Before you list items from each category,
use a confirmation dialog to ask the customer whether he or she wants to
order an item from that category.
16. Extend the program in Exercise 15 so that customers can order more than
one item from each menu category. For example, the customer can buy two
orders of Tofu Burgers and three orders of Buffalo Wings from the Entree
menu category.
17. Complete the loan table program discussed in Section 6.8.
18. Implement the describeRules method of the Ch6HiLo class from Section 6.9.
Use a confirmation dialog to ask the user whether or not to display the game
rules.
19. The price table for carpet we printed out in Section 6.6 contains index values
for width and length, but not labels to identify them. Write an application to
generate the table shown next:
358 Chapter 6 Repetition Statements
wu23399_ch06.qxd 12/14/06 17:54 Page 358
20. Extend the HiLo class to allow the user to designate the lower and upper
bounds of the secret number. In the original HiLo class, the bounds are set to
1 and 100, respectively.
21. A formula to compute the Nth Fibonacci number was given in Exercise 19 in
Chapter 3. The formula is useful in finding a number in the sequence, but a
more efficient way to output a series of numbers in the sequence is to use the
recurrence relation FN  FN1  FN2, with the first two numbers in the
sequence F1 and F2 both defined as 1. Using this recurrence relation, we can
compute the first 10 Fibonacci numbers as follows:
F1 = 1
F2 = 1
F3 = F2 + F1 = 1 + 1 = 2
F4 = F3 + F2 = 2 + 1 = 3
F5 = F4 + F3 = 3 + 2 = 5
F6 = F5 + F4 = 5 + 3 = 8
F7 = F6 + F5 = 8 + 5 = 13
F8 = F7 + F6 = 13 + 8 = 21
F9 = F8 + F7 = 21 + 13 = 34
F10 = F9 + F8 = 34 + 21 = 55
Write an application that accepts N, N 1, from the user and displays the
first N numbers in the Fibonacci sequence. Use appropriate formatting to
display the output cleanly.
22. Modify the application of Exercise 21 to generate and display all the
numbers in the sequence until a number becomes larger than the value
maxNumber entered by the user.
23. Improve the LoanCalculator class from Chapter 4 to accept only the valid
input values for loan amount, interest rate, and loan period. The original
LoanCalculator class assumed the input values were valid. For the exercise,
let the loan amount between $100.00 and $1,000,000.00, the interest rate
between 5 and 20 percent, and the loan period between 1 year and 30 years
be valid.
24. Extend Exercise 22 on page 292 by drawing a more realistic clock. Instead
of drawing a clock like this
Exercises 359
wu23399_ch06.qxd 12/14/06 17:54 Page 359
draw a circle at 5-min intervals as follows:
Use a for loop to draw 12 circles.
25. In the formatting examples from the chapter, we always provided a fixed
control string, such as
System.out.format(%4d, 23);
It is possible, however, to dynamically create the control string, as in
int i = 4;
System.out.format(% + i + d, 23);
Using this idea of dynamically creating a control string, write a code
fragment that outputs 50 X’s, using a separate line for each X. An X on a
single line is preceded by two more leading spaces than the X on the
previous line. The following figure shows the output for the first five lines.
26. (Optional) Write a recursive method to compute the sum of the first N
positive integers. Note: This is strictly for exercise. You should not write the
real method recursively.
27. (Optional) Write a recursive method to compute the sum of the first N
positive odd integers. Note: This is strictly for exercise. You should not write
the real method recursively.
Development Exercises
For the following exercises, use the incremental development methodology to
implement the program. For each exercise, identify the program tasks, create a
design document with class descriptions, and draw the program diagram. Map out
the development steps at the start. Present any design alternatives and justify your
selection. Be sure to perform adequate testing at the end of each development step.
X
X
X
X
X
360 Chapter 6 Repetition Statements
wu23399_ch06.qxd 12/14/06 17:54 Page 360
28. Write an application that draws nested N squares, where N is an input to the
program. The smallest square is 10 pixels wide, and the width of each
successive square increases by 10 pixels. The following pattern shows seven
squares whose sides are 10, 20, 30, . . . , and 70 pixels wide.
29. The monthly payments for a given loan are divided into amounts that apply
to the principal and to the interest. For example, if you make a monthly
payment of $500, only a portion of the $500 goes to the principal and the
remainder is the interest payment. The monthly interest is computed by
multiplying the monthly interest rate by the unpaid balance. The monthly
payment minus the monthly interest is the amount applied to the principal.
The following table is the sample loan payment schedule for a 1-year loan of
$5000 with a 12 percent annual interest rate.
Payment Unpaid Total Interest
No. Interest Principal Balance to Date
1 50.00 394.24 4605.76 50.00
2 46.06 398.19 4207.57 96.06
3 42.08 402.17 3805.40 138.13
4 38.05 406.19 3399.21 176.19
5 33.99 410.25 2988.96 210.18
6 29.89 414.35 2574.61 240.07
7 25.75 418.50 2156.11 265.82
8 21.56 422.68 1733.42 287.38
9 17.33 426.91 1306.51 304.71
10 13.07 431.18 875.34 317.78
11 8.75 435.49 439.85 326.53
12 4.40 439.85 0.00 330.93
Write an application that accepts a loan amount, annual interest rate,
and loan period (in number of years) and displays a table with five columns:
payment number, the interest and principal paid for that month, the
remaining balance after the payment, and the total interest paid to date.
Drawing Board
not drawn to scale
Exercises 361
wu23399_ch06.qxd 12/14/06 17:54 Page 361
Note: The last payment is generally different from the monthly payment,
and your application should print out the correct amount for the last
payment. Use the Format class to align the output values neatly.
30. Instead of dropping a watermelon from a building, let’s shoot it from a
cannon and compute its projectile. The (x, y) coordinates of a watermelon at
time t are
x  V cos( ) . t
y  V sin( ) . t 
g
2
.t2

where g is the acceleration of gravity, V is the initial velocity, and (alpha)
is the initial angle. The acceleration of gravity on earth is 9.8 m/s2
.
Write an application that inputs an initial velocity V (m/s) and an initial
angle alpha (degrees) and computes the projectile of a watermelon
cannon ball. The program should repeat the computation until the user wants
to quit. The program outputs the (x, y) oordinate value for every second,
that is, t = 0, 1, 2, and so forth. The program stops the output when the y
value becomes 0 or less. To use the cos and sin methods of the Math class,
don’t forget that you have to convert the input angle given in degrees to
radians. You can convert a degree to equivalent radians by using the
following
Radian  
degr
1
e
8
e
0
 

or calling the toRadians method of the Math class. Note: Air resistance is not
considered in the formula. Also, we assumed the watermelon will not get
smashed upon firing.
31. Write an application that simulates a slot machine. The player starts out with
M coins. The value for M is an input to the program, and you charge 25 cents
per coin. For each play, the player can bet 1 to 4 coins. If the player enters 0
as the number of coins to bet, then the program stops playing. At the end of
the game, the program displays the number of coins left and how much the
player won or lost in the dollar amount. There are three slots on the machine,
and each slot will display one of the three possible pieces: BELL, GRAPE,
x
y
(x, y)

362 Chapter 6 Repetition Statements
wu23399_ch06.qxd 12/14/06 17:54 Page 362
and CHERRY. When certain combinations appear on the slots, the machine
will pay the player. The payoff combinations are as follows:
Payoff (Times
the Betting
No. Combination Amount)
1 BELL BELL BELL 10
2 GRAPE GRAPE GRAPE 7
3 CHERRY CHERRY CHERRY 5
4 CHERRY CHERRY ----------- 3
5 CHERRY ----------- CHERRY 3
6 ----------- CHERRY CHERRY 3
7 CHERRY ----------- ----------- 1
8 ----------- CHERRY ----------- 1
9 ----------- ----------- CHERRY 1
The symbol ----------- means any piece. If the player bets 4 coins and get
combination 5, for example, the machine pays the player 12 coins.
Exercises 363
wu23399_ch06.qxd 12/14/06 17:54 Page 363
wu23399_ch06.qxd 12/14/06 17:54 Page 364
Defining Your Own
Classes—Part 2
O b j e c t i v e s
After you have read and studied this chapter,you should be able to
• Define overloaded methods and constructors.
• Describe the uses of the reserved word this.
• Define class methods and variables.
• Describe how the arguments are passed to the
parameters in method definitions with the
pass-by-value scheme.
• Describe how objects are returned from
methods.
• Document classes with javadoc comments.
• Organize classes into a package.
365
7
wu23399_ch07.qxd 12/15/06 19:53 Page 365
366 Chapter 7 Defining Your Own Classes—Part 2
I n t r o d u c t i o n
In Chapter 4, we covered the basics of programmer-defined classes with illustrative
examples. There we focused our attention on straightforward cases. After seeing
more sample programs in Chapters 5 and 6, we are now ready to attack advanced
topics of programmer-defined classes. In addition to introducing several new topics,
we will revisit some of the topics from Chapter 4 and provide a more in-depth dis-
cussion. In Chapters 5 and 6, we used the Fraction class to illustrate some of the
concepts. We will continue to use the Fraction class in this chapter to illustrate the
key concepts introduced here. Toward the end of this chapter, we will provide a
complete definition of the Fraction class. In addition to this Fraction class, we will
go over other sample classes to help students master the key concepts.
7.1 Returning an Object from a Method
Up until now, when we define a value-returning method, we return either primitive
data types, such as int or boolean, or a String. In this section, we learn how to return
objects from methods. Again, a String is an object, so in a sense, we know how to re-
turn an object from a method. However, a String is treated much as a primitive
datum for the most part. Here, we provide a more complete picture of what is going
on when we return an object of other standard and programmer-defined classes.
We use the Fraction class to illustrate the returning of an object from a
method. Here’s the portion of the class definition that includes the constructor,
accessors, and mutators (we will add other methods gradually as we cover more
topics):
class Fraction {
private int numerator;
private int denominator;
public Fraction(int num, int denom) {
setNumerator(num);
setDenominator(denom);
}
public int getDenominator( ) {
return denominator;
}
public int getNumerator( ) {
return numerator;
}
return objects
from methods
We assume both
parameters are
nonnegative.
We remove this
assumption
when listing the
final version in
Section 7.8.
wu23399_ch07.qxd 12/15/06 19:53 Page 366
public void setDenominator(int denom) {
if (denom == 0) {
//Fatal error
System.err.println(Fatal Error);
System.exit(1);
}
denominator = denom;
}
public void setNumerator(int num) {
numerator = num;
}
public String toString( ) {
return getNumerator() + / + getDenominator();
}
//other methods come here
}
Notice that we do not allow a fraction to have 0 as its denominator. If there is an
attempt to assign 0 as a fraction’s denominator, we will terminate the whole pro-
gram. This is quite a draconian measure, but we will do it this way until we learn
exception handling in Chapter 8.
Now, let’s study the simplify method that reduces a fraction to its simplest
form. How about the following?
public void simplify( ) {
int num = getNumerator();
int denom = getDenominator();
int gcd = gcd(num, denom);
setNumerator(num/gcd);
setDenominator(denom/gcd);
}
We use the gcd method that returns the greatest common divisor (int) as de-
scribed in Chapter 6. We get the simplified form by dividing the numerator and the
denominator by their greatest common divisor. Here’s a sample use of the method:
Fraction f1 = new Fraction(24, 36);
f1.simplify( ); //f1 is changed!
Notice that the value of f1 is now changed because the method updates the data
members of the receiving object (in this example, f1 is a receiving object because
we are calling the add method of f1).
Is it acceptable to change the values of the receiving object f1? In this case,
is better to keep the values of f1 unchanged and to return a Fraction object that is
in the simplified form. This will give flexibility to client programmers. Here’s the
7.1 Returning an Object from a Method 367
wu23399_ch07.qxd 12/15/06 19:53 Page 367
improved version of the simplify method. (We will make an additional improvement
in Section 7.8 to properly handle the case when gcd is zero.)
public Fraction simplify( ) {
int num = getNumerator();
int denom = getDenominator();
int gcd = gcd(num, denom);
Fraction simp = new Fraction(num/gcd, denom/gcd);
return simp;
}
The following is the sample use of the improved version:
Fraction f1, f2;
f1 = new Fraction(24, 36);
f2 = f1.simplify( );
System.out.println(f1.toString() + can be reduced to  +
f2.toString());
The output will be
24/36 can be reduced to 2/3
Be aware that we can produce such output easily because we did not change the val-
ues of f1. Now, if we really want to reduce f1 itself to simplest form, then we just
have to assign the result back to f1 as in
f1 = f1.simplify( );
Let’s study the effect of the simplify method when it returns an object. Fig-
ure 7.1 shows the state of memory after the simp object is created and assigned the
values. Notice that simp is a local variable, but the object itself is created in the heap
memory. The return statement at the end of the simplify method returns the value of
simp, which is a reference to the Fraction object. Figure 7.2 shows the state after the
simplify method is complete. The value of simp is assigned to f2, and now the vari-
able f2 points to this Fraction object. It is critical to realize that when we say return
an object from a method, we are in fact returning a reference to the object.
368 Chapter 7 Defining Your Own Classes—Part 2
When we say“return an object from a method,”we are actually returning the
address,or reference,of an object to the caller.
wu23399_ch07.qxd 12/15/06 19:54 Page 368
7.1 Returning an Object from a Method 369
f2 = f1.simplify();
public Fraction simplify() {
Fraction simp;
...
simp = new Fraction(...);
return simp;
}
At before return
1
1
f1
f2
:Fraction
numerator
denominator
24
36
:Fraction
numerator
denominator
2
3
simp
Only the local variable
simp is shown here.
Figure 7.1 This illustration shows the state after the simp object is created and assigned with the correct values.
f2 = f1.simplify();
public Fraction simplify() {
Fraction simp;
...
simp = new Fraction(...);
return simp;
}
At after simplify is complete
2
2
f1
f2
:Fraction
numerator
denominator
24
36
:Fraction
numerator
denominator
2
3
simp
Dotted lines indicate
the memory space is
deallocated.
The value of simp, which is a reference
to the Fraction object, is returned to
the caller and assigned the variable f2.
Memory space for simp is deallocated upon
the completion of the method.
Figure 7.2 Continuation of Figure 7.1.This illustration shows how an object (actually the reference to it) is
returned to the calling program.
wu23399_ch07.qxd 12/15/06 19:54 Page 369
We will be seeing more examples of object-returning methods in this and later
chapters.
370 Chapter 7 Defining Your Own Classes—Part 2
It is not necessary to create an object for every variable we use. Many novice pro-
grammers often make this mistake.For example,we write
Fraction f1, f2;
f1 = new Fraction(24, 36);
f2 = f1.simplify( );
We didn’t write
Fraction f1, f2;
f1 = new Fraction(24, 36);
f2 = new Fraction(1, 1); //not necessary
f2 = f1.simplify( );
because it is not necessary.The simplify method returns a Fraction object, and in
the calling program, all we need is a name we can use to refer to this returned
Fraction object.Don’t forget that the object name (variable) and the actual object
instance are two separate things.
1. What’s wrong with the following declaration?
class Question {
Person student;
public void getStudent( ) {
return student;
}
...
}
2. Define a Vehicle class. It has a data member owner of type Person. Include
an accessor to retrieve the owner person and a mutator to set the
owner.
7.2 The Reserved Word this
Let’s continue the implementation of the Fraction class. We now consider the four
arithmetic operations for fractions; see Figure 7.3. When defining the methods for
the four arithmetic operations, we introduce the use of the reserved word this. The
wu23399_ch07.qxd 12/15/06 19:54 Page 370
reserved word this is called a self-referencing pointer because it is used to refer to
the receiving object of a message from within this object’s method.
Let’s start with the add method that adds two fractions:
public Fraction add( Fraction frac) {
int a, b, c, d;
Fraction sum;
a = this.getNumerator(); //get the receiving
b = this.getDenominator(); //object's num and denom
c = frac.getNumerator(); //get frac's num
d = frac.getDenominator(); //and denom
sum = new Fraction(a*d + b*c, b*d);
return sum;
}
Let’s first look at how this add method is used. The following code adds two
fractions f1 and f2 and assigns the sum to f3:
Fraction f1, f2, f3;
f1 = new Fraction(1, 2);
f2 = new Fraction(1, 4);
f3 = f1.add(f2);
System.out.println(Sum of  + f1.toString() +  and  +
f2.toString() +  is  +
f3.toString();
This code, when executed, will produce the following output:
Sum of 1/2 and 1/4 is 6/8
7.2 The Reserved Word this 371
self-
referencing
pointer
Addition a
b
c
d
ad  bc
bd
 
a
b
c
d
ad
bc
  a
b
c
d
ac
bd
 
a
b
c
d
ad  bc
bd
 
Subtraction
Division Multiplication
Figure 7.3 Rules for adding,subtracting,multiplying,and dividing fractions.
Explicit use of the
reserved word this
Not simplified because
the simplify method is
not called.
wu23399_ch07.qxd 12/15/06 19:54 Page 371
In the statement
f3 = f1.add(f2);
we are calling the add method of f1 and passing the argument f2. So in this case, the
receiving object is f1. Figure 7.4 shows the state of memory at the point where the
add method of f1 is called. Notice that the self-referencing pointer this is referring
to f1 because it is the receiving object.
Because f2 is also a Fraction object, we can write the statement as
f3 = f2.add(f1);
and get the same result (since the operation is addition). In this case, the receiv-
ing object is f2, and the argument is f1. Figure 7.5 shows the state of memory at
the point where the add method of f2 is called. Notice how the objects referenced
by frac and this are swapped. This time the self-referencing pointer this is refer-
ring to f2.
372 Chapter 7 Defining Your Own Classes—Part 2
f3 = f1.add(f2);
public Fraction add(Fraction frac) {
Fraction sum;
...
sum = new Fraction(...);
return sum;
}
At when the method is called
this points to f1
1
1
f2
f1
f3
:Fraction
numerator
denominator
1
4
:Fraction
numerator
denominator
1
2
frac
this
Figure 7.4 This illustration shows the state of memory for f1.add(f2).
wu23399_ch07.qxd 12/15/06 19:54 Page 372
7.2 The Reserved Word this 373
f3 = f2.add(f1);
public Fraction add(Fraction frac) {
Fraction sum;
...
sum = new Fraction(...);
return sum;
}
At when the method is called
this points to f2
1
1
f2
f1
f3
:Fraction
numerator
denominator
1
4
:Fraction
numerator
denominator
1
2
frac
this
Figure 7.5 This illustration shows the state of memory for f2.add(f1).
The add method computes the sum of the receiving Fraction object and the
argument Fraction object. We used the identifier frac for the parameter, so
c = frac.getNumerator();
d = frac.getDenominator();
will retrieve the numerator and the denominator of the argument Fraction
object.
To retrieve the numerator and the denominator of the receiving Fraction
object, we write
a = this.getNumerator();
b = this.getDenominator();
because the reserved word this refers to the receiving object.
wu23399_ch07.qxd 12/15/06 19:54 Page 373
The use of the reserved word this is actually optional. If we do not include it
explicitly, then the compiler will insert the reserved word for us. For example, if we
write
class Sample {
public void m1( ) {
...
}
public void m2( ) {
m1();
}
}
then the compiler will interpret the definition as
class Sample [
public void m1( ) {
...
}
public void m2( ) {
this.m1();
}
}
This is the reason why we were able to call a method from another method of
the same class without the use of dot notation. In fact, it was dot notation with the
reserved word this. We will discuss the use of this when referring to data members
of class at the end of this section.
The methods for the other three arithmetic operations are defined in a similar
manner:
public Fraction divide(Fraction frac) {
int a, b, c, d;
Fraction quotient;
a = this.getNumerator();
b = this.getDenominator();
c = frac.getNumerator();
d = frac.getDenominator();
quotient = new Fraction(a*d, b*c);
return quotient;
}
374 Chapter 7 Defining Your Own Classes—Part 2
The reserved word this is
added by the compiler.
wu23399_ch07.qxd 12/15/06 19:54 Page 374
public Fraction multiply(Fraction frac) {
int a, b, c, d;
Fraction product;
a = this.getNumerator();
b = this.getDenominator();
c = frac.getNumerator();
d = frac.getDenominator();
product = new Fraction(a*c, b*d);
return product;
}
public Fraction subtract(Fraction frac) {
int a, b, c, d;
Fraction diff;
a = this.getNumerator();
b = this.getDenominator();
c = frac.getNumerator();
d = frac.getDenominator();
diff = new Fraction(a*d - b*c, b*d);
return diff;
}
We could have defined the four arithmetic methods as void methods, instead
of returning the result as a Fraction object. But doing so will severely limit the flex-
ibility. Because the methods are object-returning methods, we can write a statement
such as
f3 = f1.add(f2);
that reflects the mathematical expression
f3 = f1 + f2
naturally. Moreover, because the add method returns a Fraction object as the sum,
we can compose the calls to implement multiple additions. For example, the math-
ematical expression
f4 = f1 + f2 + f3
can be written as
f4 = f1.add( f2.add(f3) );
7.2 The Reserved Word this 375
wu23399_ch07.qxd 12/15/06 19:54 Page 375
Bad Version
where the sum of f2 and f3 is passed as an argument to the add method of f1. We can
also write the expression as
f4 = f1.add(f2).add(f3);
because f1.add(f2) refers to a (unnamed) Fraction object, and we can call this un-
named object’s add method with f3 as an argument.
Another Use of this
Consider the following class declaration:
class MusicCD {
private String artist;
private String title;
private String id;
public MusicCD(String name1, String name2) {
artist = name1;
title = name2;
id = artist.substring(0,2) + - +
title.substring(0,9);
}
...
}
The constructor has two String parameters, one for the artist and another for the
title. An id for a MusicCD object is set to be the first two letters of the artist name
followed by a hyphen and the first nine letters of the title.
Now, consider what happens if we include (say, inadvertently) a local decla-
ration for the identifier id like this:
public MusicCD(String name1, String name2) {
String id;
artist = name1;
title = name2;
id = artist.substring(0,2) + - +
title.substring(0,9);
}
Because there is a matching local declaration for id, the identifier refers to the local
variable, not to the third data member anymore. When an identifier is encountered
in a method, the following rules are applied to determine the association.
376 Chapter 7 Defining Your Own Classes—Part 2
This id is now a
local variable.
Local declaration
for id.
wu23399_ch07.qxd 12/15/06 19:54 Page 376
Why does the Java compiler not catch such an error? When would anyone want
to use the same identifier for both the data member and the local variable or parame-
ter? It is true that we strongly recommend to always use an identifier different from
any data member in declaring a local variable. But there is a situation in which we
may want to use the same identifier for a parameter and a data member. In the
MusicCD constructor, we declared the parameters name1 and name2 to avoid naming
conflict. It would actually be more meaningful to use the conflicting identifiers artist
and title. To do so, we rewrite the method by using the reserved word this as follows.
public MusicCD(String artist, String title) {
this.artist = artist;
this.title = title;
id = artist.substring(0,2) + - +
title.substring(0,9);
}
Following the stated rules, the identifier artist refers to the parameter. To refer
to the data member artist from within this constructor, we prefix the identifier artist
with the reserved word this, using dot notation, as this.artist. In the modified con-
structor, we did not use the reserved word this to refer to the data member id because
it was not necessary. Its use is optional, so we could have written
this.id = artist.substring(0,2) + - +
title.substring(0,9);
to make the code look more consistent. The reserved word this can always be used to
refer to the receiving object’s data members, whether there is a naming conflict or not.
7.2 The Reserved Word this 377
Rules for associating an identifier to a local variable,a parameter,and a data
member
1. If there’s a matching local variable declaration or a parameter,then the
identifier refers to the local variable or the parameter.
2. Otherwise,if there’s a matching data member declaration,then the identifier
refers to the data member.
3. Otherwise,it is an error because there’s no matching declaration.
This refers to the
data member.
This refers to the
parameter.
Optionally,dot notation with the reserved this can be used to refer to an object’s
data member from the object’s methods and constructors.
wu23399_ch07.qxd 12/15/06 19:54 Page 377
In general, following the common practice, we do not use dot notation (with
the reserved word this) to refer to an object’s data members from the object’s meth-
ods unless it is necessary.
Note that we can also avoid the naming conflict and still use a meaningful
name for a parameter by prefixing an article to the parameter. For example, we
could use the identifiers anArtist and aTitle instead of name1 and name2. This nam-
ing will not conflict with the data members, so the use of the reserved word this is
not necessary in the constructor. As long as you use meaningful identifiers, which
technique you adopt to avoid naming conflict is more of a personal preference.
378 Chapter 7 Defining Your Own Classes—Part 2
1. Write a single statement to express the following operations on fractions, using
the methods from the Fraction class.
f5 = (f1 + f2) / (f3 - f4)
2. If the add method is defined thus
public void add(Fraction frac) {
int a, b, c, d;
a = this.getNumerator(); //get this fraction's
b = this.getDenominator(); //num and denom
c = frac.getNumerator(); //get frac's num
d = frac.getDenominator(); //and denom
setNumerator(a*b + c*b); //updates this
setDenominator(b*d); //fraction's num and denom
}
why is it wrong to call the method as follows?
f3 = f1.add(f2);
3. Write statements to assign the sum of fractions f1 and f2 to fraction f3,
using the add method defined in question 2.
7.3 Overloaded Methods and Constructors
Let’s continue to improve the Fraction class. Given the Fraction class in its cur-
rent form, how can a client programmer express the following mathematical
expression?
f3 = 2/3 + 9
wu23399_ch07.qxd 12/15/06 19:54 Page 378
One way is to convert the integer 9 to a fraction 9/1 and then use the add method.
Fraction f1, f2, f3;
f1 = new Fraction(2, 3);
f2 = new Fraction(9, 1);
f3 = f1.add(f2);
This is not bad, but it would be nicer if we could write something like this:
Fraction f1, f3;
f1 = new Fraction(2, 3);
f3 = f1.add(9);
In other words, instead of passing a Fraction object, we want to pass a simple inte-
ger value. Of course, with the current Fraction class, the statement
f3 = f1.add(9);
will result in an error because no matching method is defined in the class. So what
we want here is two versions of addition, one that accepts a Fraction object and
another that accepts an integer. Here are the two definitions:
//Version 1
public Fraction add(Fraction frac) {
//same as before
}
//Version 2
public Fraction add(int number) {
Fraction sum;
int a, b, c, d;
a = getNumerator();
b = getDenominator();
c = number;
d = 1;
sum = new Fraction(a*d + c*b, b*d);
return sum;
}
With the second add method, we now have two methods in the class that have
the same name. This is not a problem as long as certain rules are met. The methods
having the same name are called overloaded methods.
7.3 Overloaded Methods and Constructors 379
Including d here is redundant because its
value is 1. We include it here anyway for
the sake of clarity.
overloaded
methods
wu23399_ch07.qxd 12/15/06 19:54 Page 379
Bad Version
Multiple methods can share the same name as long as one of the following
rules is met:
1. They have a different number of parameters.
2. The parameters are of different data types when the number of parameters is
the same.
The two add methods of the Fraction class satisfy the second rule. The fol-
lowing is an example in which two methods satisfy the first rule:
public void myMethod(int x, int y) { ... }
public void myMethod(int x) { ... }
More formally, we say two methods can be overloaded if they do not have the
same signature. The method signature refers to the name of the method and the
number and types of its parameters. The two myMethod methods have different sig-
natures because the data types of the second parameter are different.
Two methods cannot be overloaded just by the different return types because
two such methods would have the same signature. For example, the following two
methods cannot be defined in the same class:
public double getInfo(String item) { ... }
public int getInfo(String item) { ... }
Now, let’s look at the second add method again. Instead of defining it as we
have, we can define it by calling the first add method. Here’s how:
//More concise Version 2
public Fraction add(int number) {
Fraction frac = new Fraction(number, 1);
Fraction sum = add(frac); //calls the first add method
return sum;
}
In defining overloaded methods, it is common for one of them to call another. Such
implemenation indicates their relationship clearly—that they are different versions
of the same logical operation. It also makes the modification easier because we need
to change the code in only one method. Other methods calling this method require
no changes. We can define the overloaded methods for the other three arithmetic
operations in a similar manner.
Overloading Constructors
Up until now, our programmer-defined classes included exactly one constructor.
But a constructor is also a method, so it, too, can be overloaded. Indeed, it is much
380 Chapter 7 Defining Your Own Classes—Part 2
method
signature
wu23399_ch07.qxd 12/15/06 19:54 Page 380
more common to define multiple constructors in a programmer-defined class. The
same rules for overloaded methods apply. Defining multiple constructors for a class
gives the client programmer flexibility in creating instances. The client programmer
can pick one of the several constructors that is suitable for her needs at hand. Let’s
define multiple constructors for the Fraction class. Here are the four constructors
(including the one already defined before at the bottom):
public Fraction( ) { //creates 0/1
setNumerator(0);
setDenominator(1);
}
public Fraction(int number) { //creates number/1
setNumerator(number);
setDenominator(1);
}
public Fraction(Fraction frac) { //copy constructor
setNumerator(frac.getNumerator());
setDenominator(frac.getDenominator());
}
public Fraction(int num, int denom) {
setNumerator(num);
setDenominator(denom);
}
The third constructor that accepts a Fraction object and creates a copy of the
passed Fraction object is called a copy constructor. A copy constructor can be quite
handy when we need to create instances of a class that includes many data mem-
bers. Often we want to create a copy before changing the values of or experiment-
ing with the original object.
As another example, here’s a Bicycle class with two constructors that initialize
the two data members:
class Bicycle {
// Data Members
private String id;
private String ownerName;
// Constructors
public Bicycle( ) {
id = XXXX-XXXX;
ownerName = Unassigned;
}
7.3 Overloaded Methods and Constructors 381
multiple
constructors
copy
constructor
wu23399_ch07.qxd 12/15/06 19:54 Page 381
public Bicycle(String tagNo, String name) {
id = tagNo;
ownerName = name;
}
//the rest of the class
. . .
}
Calling a Constructor From Another Constructor by Using this
The last use of the reserved word this is to call a constructor from another con-
structor of the same class. Here’s how we can rewrite the four constructors of the
Fraction class by using the reserved word this:
public Fraction( ) { //creates 0/1
this(0, 1);
}
public Fraction(int number) { //creates number/1
this(number, 1);
}
public Fraction(Fraction frac) { //copy constructor
this( frac.getNumerator(),
frac.getDenominator() );
}
public Fraction(int num, int denom) {
setNumerator(num);
setDenominator(denom);
}
The syntax for calling a constructor from another constructor of the same
class is
this( parameter-list );
The constructor that matches the parameter list will be called. We can add more
statements after the this statement in a constructor, but not before it. In other words,
the call to this in a constructor must be the first statement of the constructor.
382 Chapter 7 Defining Your Own Classes—Part 2
This constructor is
called by the other
three constructors.
When you use this to call a constructor from another constructor of the same class,
the this statement must be the first statement in the constructor.
wu23399_ch07.qxd 12/15/06 19:54 Page 382
7.4 Class Variables and Methods 383
1. Are there any conflicts in the following three constructors for ClassX to be
valid?
public ClassX( int X ) {
...
}
public ClassX( float X ) {
...
}
public ClassX( int Y ) {
...
}
2. Define a Student class. A Student has a name. Define two constructors, one
with no argument and another with the name as its argument. Initialize the
name to a default value Unknown for the zero-argument constructor.
3. Rewrite the following constructors, so the first one calls the second one.
public ClassOne(int alpha) {
this.alpha = alpha;
this.beat = 0;
}
public ClassOne(int alpha, int beta) {
this.alpha = alpha;
this.beta = beta;
}
7.4 Class Variables and Methods
We introduced the concepts of class methods, class variables, and class constants in
Chapter 1. We saw how class constants are declared in the actual Java statements in
Chapter 4. We complete our study of class components in this section by describing
how class methods and class variables are used in Java programs. Let’s begin with
the class methods.
The Math class includes a class method called min to compare two numbers.
We use this class method as follows:
int i, j, smaller;
i = ...;
j = ...;
smaller = Math.min(i, j);
wu23399_ch07.qxd 12/15/06 19:54 Page 383
Now suppose we want to have a method to find the smaller of two Fraction objects.
Where do we define such a method? The logical place is, of course, the Fraction
class. But will this method be an instance method? No, a class method, which fol-
lows the pattern of the min method of the Math class, is most appropriate. We can
define a class method called min that accepts two Fraction objects as arguments and
returns the smaller fraction. Here’s how we define the min method:
class Fraction {
...
public static Fraction min(Fraction f1, Fraction f2) {
//convert to decimals and then compare
double f1_dec = f1.decimal();
double f2_dec = f2.decimal();
if ( f1_dec = f2_dec) {
return f1;
} else {
return f2;
}
}
private double decimal( ) {
//returns the decimal equivalent
return (double) getNumerator() / getDenominator();
}
...
}
The reserved word static indicates that the min method is a class method. A
class method is called by using dot notation with the class name. Here’s a sample use:
Fraction f1, f2, smaller;
f1 = new Fraction(1, 6);
f2 = new Fraction(4, 5);
smaller = Fraction.min(f1, f2);
Remember, in Chapter 6 we discussed the need for finding the greatest com-
mon divisor of two integers to simplify a given fraction. Following the logic of the
min method, we can define the gcd method as a class method. Here’s how:
public static int gcd(int m, int n) {
//the code implementing the Euclidean algorithm
}
384 Chapter 7 Defining Your Own Classes—Part 2
wu23399_ch07.qxd 12/15/06 19:54 Page 384
Notice that the arguments to this method are two integers. When this method is
called from another method of the Fraction class, the numerator and the denomina-
tor are passed as the arguments. We declare this method public so the client pro-
grammers of the Fraction class can use it also. If this is not necessary, then we can
declare it private. ( Note: Logically, the gcd method should be a class method, but
there will be no serious consequences if we define it as an instance method.)
In a manner similar to the min and gcd methods, we can define the methods
for arithmetic operations as class methods. For example, here’s how:
public static Fraction add(Fraction f1, Fraction f2) {
int a, b, c, d;
Fraction sum;
a = f1.getNumerator();
b = f1.getDenominator();
c = f2.getNumerator();
d = f2.getDenominator();
sum = new Fraction(a*d + b*c, b*d);
return sum;
}
To use this class method, we write something like this:
Fraction x = new Fraction(1, 8);
Fraction y = new Fraction(4, 9);
Fraction sum = Fraction.add(x, y);
The class method add, however, becomes awkward when we try to compose
additions. To add three fractions x, y, and z, for example, we have to write
Fraction sum = Fraction.add(Fraction.add(x,y), z);
The instance method add, as we defined at the beginning of the chapter,
allows a lot more natural and flexible use.
Now let’s look at an example of class variables (we have been using class con-
stants since Chap. 4). Suppose we want to assign a tag number automatically when
a new instance of the Bicycle class is created. We want the tag numbers to be
ABC-101, ABC-102, ABC-103, and so forth. What we need to define in the Bicycle
class is a counter that counts up from 101. Only one counter is necessary for the
whole class, so it is logical to define this counter as a class variable.
First, we declare and initialize the class variable counter:
class Bicycle {
private static int counter = 101;
...
}
7.4 Class Variables and Methods 385
wu23399_ch07.qxd 12/15/06 19:54 Page 385
Then we adjust the constructor, so the id of a bicycle is assigned correctly. Here’s
how:
public Bicycle( ) {
id = ABC- + counter;
...
counter++;
}
Static Initializer
There are cases in which we may need to do more than a simple assignment to ini-
tialize a class variable. For example, we may be required to read the starting value
for the class variable counter of the Bicycle class from a file. If we need to perform
more than a simple assignment to initialize a class variable, then we define a static
initializer. A static initializer is a code that gets executed when a class is loaded into
the Java system. It is defined in the following manner:
class XYZ {
...
static {
//code to initialize
//class variables and perform
//other tasks
}
...
}
As an illustration, here’s how we define the static initializer for the Bicycle class to
set the starting value of counter to 101:
class Bicycle {
private static int counter;
...
static {
counter = 101;
}
...
}
We conclude this section with important reminders.
386 Chapter 7 Defining Your Own Classes—Part 2
static initializer
wu23399_ch07.qxd 12/15/06 19:54 Page 386
7.5 Call-by-Value Parameter Passing
We will provide more detailed coverage on how arguments are passed to a method.
Let’s first review some key facts. Local variables are used for temporary purposes,
such as storing intermediate results of a computation. While the data members of a
class are accessible from all instance methods of the class, local variables and para-
meters are accessible only from the method in which they are declared, and they are
available only while the method is being executed. Memory space for local vari-
ables and parameters is allocated upon declaration and at the beginning of the
method, respectively, and erased upon exiting from the method.
When a method is called, the value of the argument is passed to the matching
parameter, and separate memory space is allocated to store this value. This way of
passing the value of arguments is called a pass-by-value or call-by-value scheme.
Since separate memory space is allocated for each parameter during the execution
of the method, the parameter is local to the method, and therefore changes made to
the parameter will not affect the value of the corresponding argument.
Consider the following myMethod method of the Tester class. The method
does not do anything meaningful. We use it here to illustrate how the call-by-value
scheme works.
class Tester {
public void myMethod(int one, double two ) {
one = 25;
two = 35.4;
}
}
What will be the output from the following code?
Tester tester;
int x, y;
tester = new Tester();
x = 10;
y = 20;
tester.myMethod( x, y );
System.out.println( x +   + y );
7.5 Call-by-Value Parameter Passing 387
1. Class methods can access only the class variables and the class constants of
the class.
2. Instance methods,including constructors,can access all types of data members.
3. Class methods cannot call instance methods of the same class.
4. Instance methods can call all other methods of the same class.
call-by-value
scheme
wu23399_ch07.qxd 12/15/06 19:54 Page 387
The output will be
10 20
because with the pass-by-value scheme, the values of arguments are passed to the
parameters, but changes made to the parameters are not passed back to the argu-
ments. Figure 7.6 shows how the pass-by-value scheme works.
Notice that the arguments are matched against the parameters in the left-to-
right order; that is, the value of the leftmost argument is passed to the leftmost pa-
rameter, the value of the second-leftmost argument is passed to the second-leftmost
parameter, and so forth. The number of arguments in the method call must match
the number of parameters in the method definition. For example, the following calls
to myMethod of the Tester class are all invalid because the number of arguments
and number of parameters do not match.
tester.myMethod( 12 );
tester.myMethod( x, y, 24.5);
Since we are assigning the value of an argument to the matching parameter,
the data type of an argument must be assignment-compatible with the data type of
the matching parameter. For example, we can pass an integer argument to a float pa-
rameter, but not vice versa. In the following, the first call is valid, but the second one
is invalid:
tester.myMethod( 12, 25 );
tester.myMethod( 23.0, 34.5 );
The name of the parameter and the name of the argument can be the same.
Keep in mind, however, that the values of arguments are still passed to a method by
the pass-by-value scheme; that is, local copies are made whether the argument and
the parameter share the same name or not.
388 Chapter 7 Defining Your Own Classes—Part 2
Remember these key points about arguments and parameters:
1. Arguments are passed to a method by using the pass-by- value scheme.
2. Arguments are matched to the parameters from left to right.The data type
of an argument must be assignment-compatible with the data type of the
matching parameter.
3. The number of arguments in the method call must match the number of
parameters in the method definition.
4. Parameters and arguments do not have to have the same name.
5. Local copies,which are distinct from arguments,are created even if the
parameters and arguments share the same name.
6. Parameters are input to a method,and they are local to the method.Changes
made to the parameters will not affect the value of corresponding arguments.
wu23399_ch07.qxd 12/15/06 19:54 Page 388
7.5 Call-by-Value Parameter Passing 389
Figure 7.6 How memory space for the parameters is allocated and deallocated.
1
execution flow
Local variables do not exist
before the method execution.
x = 10;
y = 20;
tester.myMethod( x, y );
public void myMethod( int one, double two ) {
one = 25;
two = 35.4;
}
at before calling myMethod
1
1
10
x
20
y
state of memory
2
Memory space for myMethod is allocated, and the values
of arguments are copied to the parameters.
x = 10;
y = 20;
tester.myMethod( x, y );
public void myMethod( int one, double two ) {
one = 25;
two = 35.4;
}
values are copied at 2
2
10
x
20
y
10
one
20.0
two
3
The values of parameters are
changed.
x = 10;
y = 20;
tester.myMethod( x, y );
public void myMethod( int one, double two ) {
one = 25;
two = 35.4;
}
at before return
3
3
10
x
20
y
25
one
35.4
two
4
Memory space for myMethod is deallocated, and
parameters are erased. Arguments are unchanged.
x = 10;
y = 20;
tester.myMethod( x, y );
public void myMethod( int one, double two ) {
one = 25;
two = 35.4;
}
at after myMethod
4
4
10
x
20
y
wu23399_ch07.qxd 12/15/06 19:54 Page 389
Now let’s look at a similar example again, but this time with objects. Consider
the following class:
class ObjectTester {
public void swap(Fraction f1, Fraction f2) {
Fraction temp;
temp = f1; //swap the two fractions
f1 = f2;
f2 = temp;
}
}
What will be the output from the following code?
ObjectTester tester;
Fraction x, y;
tester = new ObjectTester();
x = new Fraction(1, 2);
y = new Fraction(3, 4);
tester.swap(x, y);
System.out.println(x =  + x.toString());
System.out.println(y =  + y.toString());
The output will be
x = 1/2
y = 3/4
because the changes made to the parameters are not passed back to the arguments.
It does not matter whether we are passing primitive data values or objects (actually,
references to the objects). Figure 7.7 shows the effect of calling the swap method.
Changes made to the parameters are not passed back to the arguments, but
when we are passing objects to a method, then the changes made to the object itself
are reflected back to the caller because the calling side still has the same reference
to the object. Let’s look at an example. Consider the following class:
class ObjectTester2 {
public void change(Fraction f1) {
f1.setNumerator(10);
}
}
390 Chapter 7 Defining Your Own Classes—Part 2
wu23399_ch07.qxd 12/15/06 19:54 Page 390
7.5 Call-by-Value Parameter Passing 391
Figure 7.7 This illustration shows the effect of calling the swap method.
:Fraction
numerator
denominator
3
4
:Fraction
numerator
denominator
1
2
f1
f2
x
y
:Fraction
numerator
denominator
3
4
:Fraction
numerator
denominator
1
2
f1
f2
x
y
:Fraction
numerator
denominator
3
4
:Fraction
numerator
denominator
1
2
f1
f2
x
y
At the beginning of
the swap method.
At the end of the swap
method when f1 and
f2 are swapped.
After the swap method
terminates. No changes
made to parameters f1
and f2 are reflected
back to the arguments.
Both x and y still point
to the same objects as
before the call.
wu23399_ch07.qxd 12/15/06 19:54 Page 391
What will be the output from the following code?
ObjectTester2 tester;
Fraction x;
tester = new ObjectTester();
x = new Fraction(1, 2);
tester.change(x);
System.out.println(x =  + x.toString());
The output will be
x = 10/2
Figure 7.8 shows the effect of calling the change method. Notice that the variable x
continues to point to the same object, but the object itself has been modified.
392 Chapter 7 Defining Your Own Classes—Part 2
Pass-by-value (also known as call-by-value) is the only parameter passing mech-
anism Java supports. Because we are passing references when objects are passed
to methods, many people with background in other programming languages
use the term pass by reference (or call by reference) when referring to the passing
of objects to methods.This is wrong. Pass by reference means an address (or ref-
erence) of a variable is passed, whereas pass by value means the content of a
variable is passed (and copied into a parameter). In Java, the content of a variable
is either a value of primitive data type or a reference to an object (this is the
source of confusion). But it doesn’t matter what the content of a variable is; as
long as the content of a variable is passed and copied into a parameter, it is a
call by value. If a programming language supports the pass-by-reference
mechanism,then it is possible,for example,to swap the values of two arguments
in a single method call.No such thing is possible in Java.
1. What is the name of the scheme used in Java to pass arguments to a method?
2. What is the output from the following code?
class Question {
private int one;
public void myMethod( int one ) {
this.one = one;
one = 12;
}
}
class Test {
public static void main(String[] arg) {
int one = 30;
wu23399_ch07.qxd 12/15/06 19:54 Page 392
7.5 Call-by-Value Parameter Passing 393
Figure 7.8 This illustration shows the effect of calling the change method.
:Fraction
numerator
denominator
1
2
Numerator is
changed to 10.
At the beginning of
the change method.
At the end of the
change method when
the numerator of the
Fraction object is
changed.
After the change
method terminates.
The variable x still
points to the same
object, but the object
itself has been
modified.
f1
x
:Fraction
numerator
denominator
10
2
f1
x
:Fraction
numerator
denominator
10
2
f1
x
Question q = new Question();
q.myMethod(one);
System.out.println(one);
}
}
wu23399_ch07.qxd 12/15/06 19:54 Page 393
7.6 Organizing Classes into a Package
For simplicity, we have placed all programmer-defined classes of a program in the
same folder since Chapter 4. This approach works fine while we are learning pro-
gramming and do not deal with many classes. But in a more real-life context, we
need to manage classes more effectively. For example, following the approach, we
have to copy the Fraction class to multiple folders if we want to use this class in dif-
ferent programs.
The correct approach to reusing programmer-defined classes is to organize
them into packages, just as the standard classes are organized into packages. We
illustrate the process by using the Fraction class. Let’s name the package to place the
Fraction class myutil. It is a Java convention to name the package with all low-
ercase letters. Once this package is set up correctly, we can use the classes in the
package by importing it, just as we have been doing with the standard packages.
import myutil.*;
class MyClient {
Fraction f1;
...
}
To set up the programmer-defined packages for general reuse, not just use by
the programs in the same folder, we have to perform the following tasks:
1. Include the statement
package myutil;
as the first statement of the source file for the Fraction class.
2. The class declaration must include the visibility modifier public as
public class Fraction {
...
}
3. Create a folder named myutil, the same name as the package name. In Java,
the package must have a one-to-one correspondence with the folder.
4. Place the modified Fraction class into the myutil folder and compile it.
5. Modify the CLASSPATH environment variable to include the folder that
contains the myutil folder. See below.
Step 5 is the most troublesome step for those new to Java. Since the exact
steps to change the CLASSPATH environment variable are different from each
platform (Windows, Unix, Mac) and Java IDE (Eclipse, NetBeans, jGRASP, BlueJ,
etc.), we will describe only the general idea for the Windows platform here.
Suppose we have a folder named JavaPrograms under the C: drive, and the myutil
package (folder) is placed inside this JavaPrograms folder. Then to use the classes in
394 Chapter 7 Defining Your Own Classes—Part 2
programmer-
defined
packages
wu23399_ch07.qxd 12/15/06 19:54 Page 394
the myutil package, the classpath environment should make a reference to the
JavaPrograms folder (not to the package myutil itself):
set classpath=.;c:JavaPrograms
The period after the equals symbol refers to the current folder (the folder where the
client program we are trying to execute is located). Without this reference to the cur-
rent folder, the client program will not recognize other classes in the same folder.
7.7 Using Javadoc Comments for Class Documentation 395
To make the programmer-defined packages accessible to all client programs,the
CLASSPATH environment variable must be set up correctly.
7.7 Using Javadoc Comments for Class Documentation
We mentioned in Chapter 2 that there are three styles of comments in Java. We have
been using the two of them. We introduce the third style called javadoc comments in
this section. Many of the programmer-defined classes we design are intended to be
used by other programmers. It is, therefore, very important to provide meaningful
documentation to the client programmers so they can understand how to use our
classes correctly. By adding javadoc comments to the classes we design, we can
provide a consistent style of documenting the classes. Once the javadoc comments
are added to a class, we can use a special program (comes as a part of Java 2 SDK)
to generate HTML files for documentation. (Note: An HTML file is a specially
marked file intended for a Web browser.) We mentioned in Chapter 2 that the docu-
mentation for the standard classes can be found at http://java.sun.com/j2se/1.5/
docs/api/index.html.
This documentation is derived from the javadoc comments embedded in the
standard classes.
We will describe how to use javadoc comments and generate the corresponding-
HTMLdocumentation files. Before we get into the details, we first show the end result
so you can visualize where the process is leading. Figure 7.9 shows a portion of the
HTML documentation for the Fraction class displayed in a browser.
A javadoc comment is used as header comment for a class, a data member, or
a method. Let’s begin with the class header comment for the Fraction class in the
javadoc format:
/**
* An instance of this class represents a fraction.
*
* @author Dr. Caffeine
*
*/
class Fraction {
...
}
javadoc
comments
wu23399_ch07.qxd 12/15/06 19:54 Page 395
The javadoc comments begin with the marker /** and end with the marker */. The
asterisks on the lines between the first and the last markers have no significance;
they are there to provide a visual aid to highlight the comments in the program.
It is an accepted standard to use the asterisks in this manner for the javadoc
comments.
Inside the javadoc comments, we can use a number of javadoc tags, special
markers that begin with the @ mark. In this example, we see one javadoc tag
@author, which we use to list the authors of the class.
396 Chapter 7 Defining Your Own Classes—Part 2
Figure 7.9 A browser showing the HTML documentation file derived from the javadoc comments for the
Fraction class.
javadoc tags
@author tag
wu23399_ch07.qxd 12/15/06 19:54 Page 396
Here’s how we comment a data member in javadoc:
/**
* The numerator portion of this fraction
*/
private int numerator;
When the length of a comment is short and fits in a single line, then we can write the
javadoc comment as
/** The numerator portion of this fraction */
private int numerator;
The javadoc comment for a method is similar to the one for the class header
comment. It will include a number of javadoc tags in addition to a general descrip-
tion. Here’s how a method is commented by using javadoc:
/**
* Returns the sum of this Fraction
* and the parameter frac. The sum
* returned is NOT simplified.
*
* @param frac the Fraction to add to this
* Fraction
*
* @return the sum of this and frac
*/
public Fraction add(Fraction frac) {
...
}
The purpose of the method header comment is to record the method’s pur-
pose, list of parameters passed to the method, and value returned from the method.
This method receives one parameter, so there is one @param tag. We attach a short
description of the parameter in addition to the parameter’s name. The syntax for the
@param javadoc tag is
@param parameter name description
The description portion can go beyond one line. As this method returns a value,
we add the @return tag. Its syntax is
@return description
A javadoc comment for a constructor is defined in a manner similar to the
one for a method, except there will never be an @return tag for a constructor.
Figure 7.10 shows the HTML document that is generated from this javadoc com-
ment. Notice the effect of @param and @return tags.
7.7 Using Javadoc Comments for Class Documentation 397
@param tag
@return tag
wu23399_ch07.qxd 12/15/06 19:54 Page 397
The use of the javadoc comments does not preclude the use of other types of
comments. We still need to use regular comments to describe the code as necessary.
For example, we will continue to include the group comment for methods, as in
so that programmers reading the class will have a handy reference to the list of
methods without referring to any online documentation. This is especially useful
when the programmers are reading a hard copy of the class source file. Notice that
we don’t use the javadoc style for a quick reference list because javadoc comments
are used only for describing the class and its data members and methods.
Once all the javadoc comments are added to a class, we are ready to generate
the corresponding HTML documentation file. For easy reference, we call it the
//––––––––––––––––––––––––––––––––––––––––––
// Public Methods:
//
// Fraction add ( Fraction )
// Fraction add ( int )
//
// ...
//––––––––––––––––––––––––––––––––––––––––––
398 Chapter 7 Defining Your Own Classes—Part 2
Figure 7.10 The portion of the HTML documentation file that is derived from the javadoc header comment
for the add method.
wu23399_ch07.qxd 12/15/06 19:54 Page 398
javadoc file. Many Java editors and IDEs include a menu option that you can use
to generate javadoc files easily and quickly. Here we describe the steps you can
take to generate javadoc files using the minimalist approach (see App. A). In the
command prompt window, we used the commands javac and java to compile and
run Java programs, respectively. Similarly, to generate javadoc files, we use the
javadoc command. For example, to generate a javadoc file for the Fraction class,
we enter
javadoc -private Fraction.java
We specify the -private option because we want to generate the documentation for
all types of methods (so far, we have covered two of these—private and public). The
-private option generates the most complete documentation. When the command is
executed, status messages such as these are displayed.
After the command is executed successfully, there will actually be a collection
of HTML files, not just the expected Fraction.html. You can view the content shown
in Figure 7.9 by opening the file index.html and clicking the Fraction link. Open the
Fraction.html directly from your browser and see the difference. We encourage you
to open other HTML files to see how these files are related. The generated HTML
files are located in the same directory where the source file Fraction.java is located.
You can change the directory by setting the -d option and specifying the directory
to store the generated HTML files (alternatively, you can move the files using an op-
erating system’s file manager). We ordinarily do not generate javadoc files one class
at a time. Rather, it is more common to generate a complete set of javadoc files for
all classes in a single package at once, as in
javadoc -private *.java
We will refer you to websites for a more complete discussion of javadoc.
7.7 Using Javadoc Comments for Class Documentation 399
wu23399_ch07.qxd 12/15/06 19:54 Page 399
7.8 The Complete Fraction Class
In this section, we will list a complete definition for the myutil.Fraction class. In the
final version of the class, we will include improvements to one of the constructors
and the simplify method. Earlier in the chapter, we presented the fourth constructor
as follows:
public Fraction(int num, int denom) {
setNumerator(num);
setDenominator(denom);
}
400 Chapter 7 Defining Your Own Classes—Part 2
General information on javadoc is located at
http://java.sun.com/j2se/javadoc
Detailed reference on how to use javadoc on Windows is located at
http://java.sun.com/j2se/1.5/docs/tooldocs/windows/javadoc.html
Is it really important to use javadoc comments? It’s true that we have to learn a few
extra items to use javadoc comments,but the benefits warrant a little extra effort.
First, by using javadoc comments, we can easily produce the standard online
documentation. Even if we don’t have an immediate need to produce an online
documentation, we can use javadoc comments because they are really not that
different from other styles of commenting, and their use gives us an option to
produce an online documentation later.Second,since javadoc is a standard,other
programmers will have an easier time reading your code with javadoc comments
than reading code with a nonstandard style of comments.
1. Add javadoc comments to the following class.
class Instructor {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName( ) {
return name;
}
}
2. What is the purpose of @author tag?
wu23399_ch07.qxd 12/15/06 19:54 Page 400
For this constructor to function properly, we made an assumption that the values
for both parameters are nonnegative. Let’s remove this assumption and make the
necessary modifications to the constructor.
Consider the following two statements:
Fraction f1 = new Fraction(-2, 9);
Fraction f2 = new Fraction(2, -9);
Both represent the same value, namely,
2
9
. With the given constructor, f1 will have
the values 2 and 9 for its data members numerator and denominator, respectively.
And f2 will have the values 2 and 9 for its data members numerator and denom-
inator, respectively. This means that we have two distinct ways to represent the
same value. It is always preferable to maintain a consistent representation because
multiple representations for the same value would lead to a more complex code for
handling different representations correctly. We will improve this constructor so
that a negative fraction is always represented by a negative value for numerator and
a positive value for denominator.
Now, consider the following two statements:
Fraction f3 = new Fraction(2, 9);
Fraction f4 = new Fraction(-2, -9);
Both objects represent the same positive fraction 
2
9
. Again, to maintain consistent
representation, a positive fraction is always represented by positive values for both
numerator and denominator.
Finally, consider the following two statements:
Fraction f3 = new Fraction(0, 9);
Fraction f4 = new Fraction(0, -5);
Both objects represent the numerical value of 0. We will always represent the numer-
ical value of 0 by storing 0 in the data member numerator and 1 in denominator.
Here’s the modified constructor:
public Fraction(int num, int denom) {
if (denom  0) {
num = -num;
denom = -denom;
}
if (num == 0) {
denom = 1;
}
setNumerator(num);
setDenominator(denom);
}
7.8 The Complete Fraction Class 401
wu23399_ch07.qxd 12/15/06 19:54 Page 401
We will also make a modification to the simplify method. The original simplify
method fails when someone tries to simplify a zero fraction (i.e., a fraction with
numerical value of 0). To reduce a fraction to its simplified form, we find the
greatest common divisor of its numerator and denominator and divide them by
the greatest common divisor. What happens when the numerator is 0? The greatest
common divisor of 0 and any other value is 0. So we would end up dividing the
numerator and denominator by 0! Here’s the new simplify method that avoids this
problem:
public Fraction simplify( ) {
int num = getNumerator();
int denom = getDenominator();
int divisor = 1;
if (num != 0) {
divisor = gcd(Math.abs(num), denom);
}
return new Fraction(num/divisor, denom/divisor);
}
402 Chapter 7 Defining Your Own Classes—Part 2
package myutil;
/**
* An instance of this class represents a fraction.
*
*
*/
public class Fraction {
/** the numerator of this fraction */
private int numerator;
/** the denominator of this fraction */
private int denominator;
//-----------------------------------------
// Constructors
//-----------------------------------------
/**
* Creates a fraction 0/1
*/
public Fraction( ) {
this(0, 1);
}
Data Members
Constructors
wu23399_ch07.qxd 12/15/06 19:54 Page 402
7.8 The Complete Fraction Class 403
/**
* Creates a fraction number/1
*
* @param number the numerator
*/
public Fraction(int number) {
this(number, 1);
}
/**
* Creates a copy of frac
*
* @param frac a copy of this parameter is created
*/
public Fraction(Fraction frac) {
this(frac.getNumerator(), frac.getDenominator());
}
/**
* Creates a fraction num/denom. Create a negative
* fraction as -num and denom. If negative values
* are specified for both num and denom, the fraction
* is converted to a positive. If num is positive and
* denom is negative, the fraction will be converted to
* have negative num and positive denom.
* When the num is zero, denom is set to 1. Zero is
* always represented as 0/1
*
* @param num the numerator
* @param denom the denominator
*/
public Fraction(int num, int denom) {
if (denom  0) {
num = -num;
denom = -denom;
}
if (num == 0) {
denom = 1;
}
setNumerator(num);
setDenominator(denom);
}
//-----------------------------------------
// Class Methods
//
//-----------------------------------------
wu23399_ch07.qxd 12/15/06 19:54 Page 403
/**
* Returns the greatest common divisor of
* the parameters m and n
*
* @param m the first number
* @param n the second number
*
* @return the greatest common divisor of m and n
*/
public static int gcd(int m, int n) {
int r = n % m;
while (r !=0) {
n = m;
m = r;
r = n % m;
}
return m;
}
/**
* Returns the smaller of the two parameters f1 and f2
*
* @param f1 the first fraction to compare
* @param f2 the second fraction to compare
*
* @return the smaller of the two parameters
*/
public static Fraction min(Fraction f1, Fraction f2) {
//convert to decimals and then compare
double f1_dec = f1.decimal();
double f2_dec = f2.decimal();
if (f1_dec = f2_dec) {
return f1;
} else {
return f2;
}
}
404 Chapter 7 Defining Your Own Classes—Part 2
gcd
min
wu23399_ch07.qxd 1/12/07 10:48 Page 404
//-----------------------------------------
// Public Instance Methods
//
//-----------------------------------------
/**
* Returns the sum of this Fraction
* and the parameter frac. The sum
* returned is NOT simplified.
*
* @param frac the Fraction to add to this
* Fraction
*
* @return the sum of this and frac
*/
public Fraction add(Fraction frac) {
int a, b, c, d;
Fraction sum;
a = this.getNumerator();
b = this.getDenominator();
c = frac.getNumerator();
d = frac.getDenominator();
sum = new Fraction(a*d + b*c, b*d);
return sum;
}
/**
* Returns the sum of this Fraction
* and the int parameter number. The sum
* returned is NOT simplified.
*
* @param number the integer to add to this
* Fraction
* @return the sum of this Fraction and number
*/
public Fraction add(int number) {
Fraction frac = new Fraction(number, 1);
Fraction sum = add(frac);
return sum;
}
7.8 The Complete Fraction Class 405
add
add
wu23399_ch07.qxd 12/15/06 19:54 Page 405
/**
* Returns the quotient of this Fraction
* divided by the parameter frac. The quotient
* returned is NOT simplified.
*
* @param frac the divisor of the division
*
* @return the quotient of this fraction
* divided by frac
*/
public Fraction divide(Fraction frac) {
int a, b, c, d;
Fraction quotient;
a = this.getNumerator();
b = this.getDenominator();
c = frac.getNumerator();
d = frac.getDenominator();
quotient = new Fraction(a*d, b*c);
return quotient;
}
/**
* Returns the quotient of this Fraction
* divided by the int parameter number. The quotient
* returned is NOT simplified.
*
* @param number the divisor
*
* @return the quotient of this Fraction divided by number
*/
public Fraction divide(int number) {
Fraction frac = new Fraction(number, 1);
Fraction quotient = divide(frac);
return quotient;
}
/**
* Compares this fraction and the parameter frac for
* equality. This method compares the two by first
* reducing them to the simplest form.
*
* @param frac the fraction object to compare
*
* @return true if this Fraction object and frac are equal
*/
406 Chapter 7 Defining Your Own Classes—Part 2
divide
divide
wu23399_ch07.qxd 12/15/06 19:54 Page 406
public boolean equals(Fraction frac) {
Fraction f1 = simplify(); //simplify itself
Fraction f2 = frac.simplify(); //simplify frac
return (f1.getNumerator() == f2.getNumerator() 
f1.getDenominator() == f2.getDenominator());
}
/**
* Returns the denominator of this fraction
*
* @return the denominator of this fraction
*/
public int getDenominator( ) {
return denominator;
}
/**
* Returns the numerator of this fraction
*
* @return the numerator of this fraction
*/
public int getNumerator( ) {
return numerator;
}
/**
* Returns the product of this Fraction
* and the parameter frac. The product
* returned is NOT simplified.
*
* @param frac the multiplier of the multiplication
*
* @return the product of this fraction
* and the parameter frac
*/
public Fraction multiply(Fraction frac) {
int a, b, c, d;
Fraction product;
a = this.getNumerator();
b = this.getDenominator();
c = frac.getNumerator();
d = frac.getDenominator();
product = new Fraction(a*c, b*d);
return product;
}
7.8 The Complete Fraction Class 407
equals
getDenominator
getNumerator
multiply
wu23399_ch07.qxd 12/15/06 19:54 Page 407
/**
* Returns the product of this Fraction
* and the int parameter number. The product
* returned is NOT simplified.
*
* @param number the multiplier
*
* @return the product of this Fraction and number
*/
public Fraction multiply(int number) {
Fraction frac = new Fraction(number, 1);
Fraction product = multiply(frac);
return product;
}
/**
* Sets the denominator of this fraction
*
* @param denom the denominator of this fraction
*/
public void setDenominator(int denom) {
if (denom == 0) {
//Fatal error
System.out.println(Fatal Error);
System.exit(1);
}
denominator = denom;
}
/**
* Sets the numerator of this fraction
*
* @param num the numerator of this fraction
*/
public void setNumerator(int num) {
numerator = num;
}
/**
* Returns a new Fraction object that is in
* the simplest form of this Fraction object. If
* this Fraction is zero, then a simple copy of
* it is returned.
*
* @return a Fraction object in the simplest form
* of this Fraction
*/
408 Chapter 7 Defining Your Own Classes—Part 2
multiply
setDenominator
setNumerator
wu23399_ch07.qxd 12/15/06 19:54 Page 408
public Fraction simplify( ) {
int num = getNumerator();
int denom = getDenominator();
int divisor = 1;
if (num != 0) {
divisor = gcd(Math.abs(num), denom);
}
return new Fraction(num/divisor, denom/divisor);
}
/**
* Returns the difference of this Fraction
* and the parameter frac. The difference
* returned is NOT simplified.
*
* @param frac the Fraction to subtract from
* this Fraction
*
* @return the difference of this and frac
*/
public Fraction subtract(Fraction frac) {
int a, b, c, d;
Fraction diff;
a = this.getNumerator();
b = this.getDenominator();
c = frac.getNumerator();
d = frac.getDenominator();
diff = new Fraction(a*d - b*c, b*d);
return diff;
}
/**
* Returns the difference of this Fraction
* and the int parameter number. The difference
* returned is NOT simplified.
*
* @param number the int value to subtract
*
* @return the difference of this and number
*/
public Fraction subtract(int number) {
Fraction frac = new Fraction(number, 1);
Fraction difference = subtract(frac);
return difference;
}
7.8 The Complete Fraction Class 409
simplify
subtract
subtract
wu23399_ch07.qxd 12/15/06 19:54 Page 409
/**
* Returns the String representation of this Fraction
*
* @return the String representation of this Fraction
*/
public String toString( ) {
return getNumerator() + / + getDenominator();
}
//-----------------------------------------
// Private Methods
//
//-----------------------------------------
//**
* Returns the decimal equivalent of this fraction
*
* @return the decimal equivalent of this fraction
*/
private double decimal( ) {
//returns the decimal equivalent
return (double) getNumerator() / getDenominator();
}
}
410 Chapter 7 Defining Your Own Classes—Part 2
toString
decimal
Library Overdue Checker
How many library books are lying around in your room waiting to be returned? How
much have you accrued in late charges on those overdue books? Let’s write a program
that computes the total charges for overdue library books. The program allows you to
input the title,overdue charge per day,maximum possible charge,and due date for each
book you enter.The due date is the only required input.The other three input values are
optional,and when they are not provided,preset default values are used by the program.
We assume that an upper limit is set for overdue charges,so your charge will not increase
beyond this limit. This limit is entered as the maximum possible charge. For example, a
library may set $1.50 as the overdue charge per day and $30 as the maximum overdue
charge for a single overdue book.We enter the overdue charge per day and the maximum
overdue charge for every book,because depending on the types of books,they could be
different. For example, a charge for books with a 3-day loan period may be much higher
than for books with a regular 2-week loan period.
After you enter information for all books, the program displays the entered book
data.Then the program will allow you to enter different return dates.For each return date
you enter,the program will display the total overdue charges.Being able to enter different
Sample Development
7.9 Sample Development
wu23399_ch07.qxd 12/15/06 19:54 Page 410
return dates will let you make an informed decision,such as“I’ll wait till tomorrow since it’s
raining heavily today and it costs only $2 more if I return them tomorrow.” (We always
encourage you to return library books promptly for the sake of fellow library users.)
A better program will warn you when there’s a looming due date so you won’t end
up paying the overdue charges.We will discuss this and other possible extensions at the
end of this section.All the possible extensions we will discuss require techniques yet to be
studied. The program we develop here is implemented by the techniques we have
already mastered and by using one simple helper class.
Problem Statement
Write an application that computes the total charges for the overdue library
books. For each library book, the user enters the due date and (optionally) the
overdue charge per day,the maximum charge,and the title.If the optional values
are not entered, then the preset default values are used. A complete list of book
information is displayed when the user finishes entering the input data.The user
can enter different return dates to compare the overdue charges.
Overall Plan
As always, we begin our overall plan for the development with the outline of program
logic.We first let the user enter the information on all books.After finishing the book data
entry, we display them as a list.Then we ask repeatedly for return dates. For each return
date entered, we provide the total overdue charge.We express the program flow as hav-
ing three tasks:
1. Get the information for all books.
2. Display the entered book data.
3. Ask for the return date and display the total charge.Repeat this step until the user
quits.
Let’s look at each task and determine objects required for handling the task. The
first step sounds simple enough, but it hides the complexity of the whole program. It
indicates the need for at least three types of objects. One is to carry out the actual input
routines, another is to retain four pieces of information for each book, and yet another is
to keep track of multiple books entered by the user. Notice that there’s no limit on the
number of books the user can enter,because putting such a limit will reduce the usability
of the program.This means we need a class to manage a collection of book information.
We have not yet learned how to manage a collection of objects (Chap. 10 covers the
topic), so we will use the helper class named BookTracker. This class is actually very
straightforward,once we learn the relevant topic.The class is written generically and does
not contain much application-specific logic.
We will define a class named LibraryBook that keeps track of book information.An
instance of this class represents a single library book. The LibraryBook class is the key
worker bee in this program. A LibraryBook object keeps track of four pieces of informa-
tion and is responsible for computing the overdue charge. Notice that the class is the
7.9 Sample Development 411
program
tasks
wu23399_ch07.qxd 12/15/06 19:54 Page 411
7.9 Sample Development—continued
most appropriate place to perform the computation of the overdue charge because it is
where all pieces of information necessary to compute the charge are stored.
We will define another class for performing the actual input routines.An instance of
this class will input the data,create a LibraryBook object with the input data,and add this
object to a BookTracker. As it uses other objects,it will be the main controller of the pro-
gram. We will name the class OverdueChecker. We will define it as an instantiable main
class.For this program,we will use console input.It is a straightforward exercise to modify
the OverdueChecker class to handle input by using JOptionPane or other types of GUI.
Now let’s study the second task of displaying the entered book data. To be
consistent with the console input, we will use console output. An OverdueChecker will
handle the output, but the data to display come from a BookTracker, as it is the one
maintaining the collection of LibraryBook objects. The BookTracker class has one
method called getBookList. This method returns the book list as a single String value.
The OverdueChecker displays the returned string on the standard output window.
Notice that the BookTracker class is not programmed to do the output directly,because
doing so will reduce its usability.By returning the book list as a String datum,the client of
the Book-Tracker class retains the option of using either console output or GUI. This
helps to keep the flexibility and increases the usability of a class.
For the last task, an OverdueChecker interacts with the user to get the return
dates. For each return date entered, it asks BookTracker for the total charge and dis-
plays the returned value.The BookTracker in turn asks individual LibraryBook objects
for their charges by calling the computeCharge method and computes the sum.This is
the reason why we must include the method named computeCharge in the
LibraryBook class that computes the overdue charge for a single book.We will discuss
the details of this and other requirements in defining the LibraryBook class shortly.
Here’s our working design document:
412 Chapter 7 Defining Your Own Classes—Part 2
program
classes
Design Document: Library OverdueChecker
Class Purpose
OverdueChecker The top-level control object that manages other objects
in the program.This is an instantiable main class,as
explained in Section 4.10.
BookTracker The predefined helper class that keeps track of library
books.
LibraryBook An instance of this class represents a single library book.
A library book for this program has four properties—
title,charge per day,maximum charge,and due date.It is
also responsible for computing the overdue charges.
Scanner The standard class for handling input routines.
wu23399_ch07.qxd 12/15/06 19:54 Page 412
Figure 7.11 is the program diagram for this program.
We will implement this program in the following five major steps:
1. Define the basic LibraryBook class.Use a test main class to confirm the
implementation of the LibraryBook class.
2. Explore the given BookTracker class and integrate it with the LibraryBook class.
Modify or extend the LibraryBook class as necessary.
3. Define the top-level OverdueChecker class.Implement the complete input
routines.Modify or extend the LibraryBook class as necessary.
4. Complete the LibraryBook class by fully implementing the overdue charge
computation.
5. Finalize the program by tying up loose ends.
Again, the development strategy we indicate here is one of the possible alterna-
tives.We could start from the skeleton main controller class OverdueChecker, as we nor-
mally did in the previous sample development examples. For this program, however, we
start with the LibraryBook class because of its importance in the program. We want to
start from the most important workhorse class.Also,before we implement any elaborate
input and output routines, we need to know how the BookTracker class works, and to
explore this helper class fully,we need the LibraryBook class.
Step 1 Development: The Basic LibraryBook class
We begin the development with the basic LibraryBook class.The main purpose in step 1
is to start with the main workhorse class to establish the foundation for the development.
Since this class is used by the BookTracker class, we need to find out the compatibility
requirements so we won’t define any methods that will violate the compatibility. There
7.9 Sample Development 413
OverdueChecker Scanner
BookTracker LibraryBook
Figure 7.11 The program diagram for the OverdueChecker program. We will implement the
OverdueChecker and LibraryBook classes. The Scanner class is the standard class for console input,
and the BookTracker class is the helper class provided for this program.
develop-
ment steps
step 1
design
wu23399_ch07.qxd 12/15/06 19:54 Page 413
7.9 Sample Development—continued
are two methods used by the BookTracker class,so we need to define them in the Library-
Book class.Here are the two required methods and their descriptions:
414 Chapter 7 Defining Your Own Classes—Part 2
Required Methods of LibraryBook
public String toString( )
Returns the String representation of itself. This string is used by the BookTracker
class to generate a complete book list.
public double computeCharge(GregorianCalendar returnDate)
Computes and returns the overdue charge for this book, given the return date.
The key design task for this step is to identify the data members for storing relevant
information and to define their accessors and mutators as appropriate. Also, we will de-
sign multiple constructors so an instance of this class can be created in a flexible manner.
We define data members for the four pieces of required information for each book
as follows:
private GregorianCalendar dueDate;
private String title;
private double chargePerDay;
private double maximumCharge;
For each of these data members,we will define the corresponding accessors and mutators.
We define four constructors. Because the due date is something that must be
assigned when a new LibraryBook is created,every constructor requires the due date as
its argument.When other optional values are not passed to the constructor, then preset
default values are assigned.We define the multiple constructors using the technique we
learned in this chapter.The signatures for these constructors are as follows:
public LibraryBook(GregorianCalendar dueDate)
public LibraryBook(GregorianCalendar dueDate,
double chargePerDay)
public LibraryBook(GregorianCalendar dueDate,
double chargePerDay,
double maximumCharge)
public LibraryBook(GregorianCalendar dueDate,
double chargePerDay,
double maximumCharge,
String title)
wu23399_ch07.qxd 12/15/06 19:54 Page 414
We won’t be using the BookTracker class in this step, so we do not have to define
the two required methods yet. However, we can use the toString method now to verify
the correct operations of constructors and other methods,so we define it now.We use the
formatting techniques learned in Section 6.8 to format the string we return from
toString. Here’s how we define the toString method:
public String toString( ) {
return String.format(
%-30s $%5.2f $%7.2f %4$tm/%4$td/%4$ty,
getTitle(), getChargePerDay(),
getMaxCharge(), dueDate.getTime());
}
A sample string returned from the method will formatted in the following manner:
Introduction to OOP with Java $ 0.75 $ 50.00 07/10/06
Alternatively, we can format the string by using the SimpleDateFormat and
DecimalFormat classes.
public String toString( ) {
String tab = t;
SimpleDateFormat sdf
= new SimpleDateFormat(MM/dd/yy);
DecimalFormat df = new DecimalFormat(0.00);
return getTitle() + tab + $  +
df.format(getChargePerDay()) + tab + $  +
df.format(getMaxCharge()) + tab +
sdf.format(dueDate.getTime());
}
We are now ready to implement the class. Here’s the step 1 code (minus javadoc
and most other comments):
7.9 Sample Development 415
step 1 code
/*
Chapter 7 Library Overdue Checker
Step 1 LibraryBook class
File: LibraryBook.java
*/
wu23399_ch07.qxd 12/15/06 19:54 Page 415
7.9 Sample Development—continued
import java.util.*;
class LibraryBook {
private static final double CHARGE_PER_DAY = 0.50;
private static final double MAX_CHARGE = 50.00;
private static final String DEFAULT_TITLE = Title unknown;
private GregorianCalendar dueDate;
private String title;
private double chargePerDay;
private double maximumCharge;
public LibraryBook(GregorianCalendar dueDate) {
this(dueDate, CHARGE_PER_DAY);
}
public LibraryBook(GregorianCalendar dueDate,
double chargePerDay) {
this(dueDate, chargePerDay, MAX_CHARGE);
}
public LibraryBook(GregorianCalendar dueDate,
double chargePerDay,
double maximumCharge) {
this(dueDate, chargePerDay,
maximumCharge, DEFAULT_TITLE);
}
public LibraryBook(GregorianCalendar dueDate,
double chargePerDay,
double maximumCharge,
String title) {
setDueDate(dueDate);
setChargePerDay(chargePerDay);
setMaximumCharge(maximumCharge);
setTitle(title);
}
public double getChargePerDay( ) {
return chargePerDay;
}
416 Chapter 7 Defining Your Own Classes—Part 2
wu23399_ch07.qxd 12/15/06 19:54 Page 416
public GregorianCalendar getDueDate( ) {
return dueDate;
}
public double getMaxCharge( ) {
return maximumCharge;
}
public String getTitle( ) {
return title;
}
public void setChargePerDay(double charge) {
chargePerDay = charge;
}
public void setDueDate(GregorianCalendar date) {
dueDate = date;
}
public void setMaximumCharge(double charge) {
maximumCharge = charge;
}
public void setTitle(String title) {
this.title = title;
}
public String toString( ) {
return String.format(
%-30s $%5.2f $%7.2f %4$tm/%4$td/%4$ty,
getTitle(), getChargePerDay(),
getMaxCharge(), dueDate.getTime());
}
}
7.9 Sample Development 417
The purpose of step 1 testing is to verify we can create LibraryBook objects using
different constructors. In addition, we check that the other methods are working cor-
rectly,especially the toString method.Here’s one possible test main class:
step 1 test
/*
Introduction to OOP with Java 4th ed., McGraw-Hill
File: Step1/Step1Main.java
*/
wu23399_ch07.qxd 12/15/06 19:54 Page 417
7.9 Sample Development—continued
import java.util.*;
class Step1Main {
public static void main( String[] args ) {
//Create three LibraryBook objects and output them
GregorianCalendar dueDate;
LibraryBook book1, book2, book3, book4;
dueDate = new GregorianCalendar(2006, Calendar.MARCH, 14);
book1 = new LibraryBook(dueDate);
dueDate = new GregorianCalendar(2006, Calendar.FEBRUARY, 13);
book2 = new LibraryBook(dueDate, 0.75);
book2.setTitle(Introduction to oop with Java);
dueDate = new GregorianCalendar(2006, Calendar.JANUARY, 12);
book3 = new LibraryBook(dueDate, 1.00, 100.00);
book3.setTitle(Java for Smarties);
dueDate = new GregorianCalendar(2006, Calendar.JANUARY, 1);
book4 = new LibraryBook(dueDate, 1.50, 230.00,
Me and My Java);
System.out.println(book1.toString());
System.out.println(book2.toString());
System.out.println(book3.toString());
System.out.println(book4.toString());
}
}
418 Chapter 7 Defining Your Own Classes—Part 2
Running this program will produce the following output on the standard output
window:
Title unknown $ 0.50 $ 50.00 03/14/06
Introduction to OOP with Java $ 0.75 $ 50.00 02/13/06
Java for Smarties $ 1.00 $ 100.00 01/12/06
Me and My Java $ 1.50 $ 230.00 01/01/06
wu23399_ch07.qxd 12/15/06 19:55 Page 418
7.9 Sample Development 419
Step 2 Development: Integrate the BookTracker Class into the Program
In the second development step, we will bring in the helper BookTracker class into the
program. Our main concern in this step is to understand how to interact with a Book-
Tracker object correctly and adjust the LibraryBook class, as necessary, to make it com-
patible with the BookTracker class.
The BookTracker class is actually a fairly straightforward class.You are encouraged
to view the source file of the class.To understand the class fully,you need to learn about an
ArrayList, a topic covered in Chapter 10.But even without this knowledge,you should be
able to understand the majority of the code when you view the source file.We will discuss
the implementation of the BookTracker class in Chapter 10. Here’s the class description:
step 2
design
BookTracker
An instance of this class maintains a list of LibraryBook objects.
public BookTracker( )
Creates a new instance of the class.
public void add( LibraryBook book )
Adds book to the book list it maintains.
public double getCharge( )
Returns the total overdue charge for the books in the list.Uses today as the
return date.
public double getCharge( GregorianCalendar returnDate )
Returns the total overdue charge for the books in the list.The parameter is the date
the book is to be returned.
public String getList( )
Returns information on all books in the list as a single string.
As stated in step 1, the BookTracker class requires two specific methods in the
LibraryBook class. We already defined the toString method. Since we will be imple-
menting the full computeCharge method in step 4, we define a stub method for this
step as
public double computeCharge( GregorianCalendar returnDate){
return 1.00; //Stub method for Step 2
}
To check our understanding on how to interact with the BookTracker class,we will
write a test main class.From this main class,we will create and add multiple book objects
to the book tracker and experiment with the getList and getCharge methods.
The only change we make to the LibraryBook class is the addition of the stub
computeCharge method, so the BookTracker class can be integrated with it. To test
step 2 code
wu23399_ch07.qxd 12/15/06 19:55 Page 419
7.9 Sample Development—continued
the BookTracker class, we define a test main class that checks the cases when the book
list is empty and has 20 books.Here’s the test main class:
420 Chapter 7 Defining Your Own Classes—Part 2
/*
Introduction to OOP with Java 4th ed., McGraw-Hill
File: Step2/Step2Main.java
*/
import java.util.*;
class Step2Main {
public static void main( String[] args ) {
//Create 20 LibraryBook objects
BookTracker bookTracker = new BookTracker();
GregorianCalendar dueDate, returnDate;
LibraryBook book;
returnDate = new GregorianCalendar(2006, Calendar.MARCH, 15);
//Check the error condition
System.out.println(Error: No books added. Return code -  +
bookTracker.getCharge(returnDate));
System.out.println(Output for empty book list:n +
bookTracker.getList( ));
//Add 20 books
System.out.println(nAdding 20 books...n);
for (int i = 0; i  20; i++) {
dueDate = new GregorianCalendar(2006, Calendar.MARCH, i+1);
book = new LibraryBook(dueDate);
book.setTitle(Book Number  + (i+1));
bookTracker.add(book);
}
System.out.println(Total Charge: $
+ bookTracker.getCharge(returnDate));
System.out.println(n);
System.out.println(List: n + bookTracker.getList());
}
}
wu23399_ch07.qxd 12/15/06 19:55 Page 420
We run the test main class and verify that we get the expected results.We will try
other variations to increase our confidence before continuing to the next step.
Step 3 Development: Define the OverdueChecker Class
After the working LibraryBook and BookTracker classes, we are now ready to start im-
plementing the top-level controller class.Besides managing a single BookTracker object
and multiple LibraryBook objects, an OverdueChecker object’s main responsibility is
the handling of input and output routines.As dictated in the problem statement,we have
to first input information on books and then repeatedly ask the user for return dates.
Expressing this logic in pseudocode,we have
GregorianCalendar returnDate;
String reply, table;
double totalCharge;
inputBooks(); //read in all book information
table = bookTracker.getList();
System.out.println(table);
//try different return dates
do {
returnDate = read return date ;
totalCharge = bookTracker.getCharge(returnDate);
displayTotalCharge(totalCharge);
reply = prompt the user to continue or not;
} while ( reply is yes );
The body of the inputBooks method will include a loop that reads information for
one book on each repetition.The method body can be expressed thus:
while (isContinue()) {
title = readString(Title : );
chargePerDay = readDouble(Charge per day: );
maxCharge = readDouble(Maximum charge: );
dueDate = readDate (Due Date : );
book = createBook(title, chargePerDay,
maxCharge, dueDate);
bookTracker.add(book);
}
Notice that there are three types of input data, and we define a method for each type,
namely, readDouble, readDate, and readString. These methods read input from a scan-
ner (console input) after prompting the user.
7.9 Sample Development 421
step 2 test
step 3
design
wu23399_ch07.qxd 12/15/06 19:55 Page 421
7.9 Sample Development—continued
Code to handle the input of String and double values is straightforward, but the
one to handle the input of the date requires some thinking.We need to decide in which
format the user can enter the date. For instance, should we prompt for the year, month,
and day individually? This may be acceptable if you enter the date once.When you have
to input date information many times,this input routine gets tedious.For this application,
we will require the user to enter the date correctly as a single string value in the
MM/dd/yyyy format. Given a string value in this format, we use a sequence of substring
methods to break it down into three pieces—month, day, and year. This operation is
similar to the one we used in the Chapter 2 sample application. Then we use the
Integer.parseInt method, introduced in Chapter 3, to convert them to int values. From
these three int values, we finally create and return a GregorianCalendar object that
represents the entered date.
After the four values are entered,a new book is created via the createBook method.
This method handles the situation when the input value is empty. For example, the user
may press only the Enter key if she wants default values for the single-day charge and
maximum possible charge.
The other methods are straightforward,so we’ll refer you to the complete class list-
ing without further explanation.Here’s the instantiable main class OverdueChecker:
422 Chapter 7 Defining Your Own Classes—Part 2
step 3 code
/*
Chapter 7 Library Overdue Checker
Step 3 Implement the Main Controller
*/
import java.util.*;
class OverdueChecker {
private static enum Response {YES, NO}
private static final String DATE_SEPARATOR = /;
private Scanner scanner;
private BookTracker bookTracker;
//-------------------------------------------------
// Constructors
//-------------------------------------------------
public OverdueChecker() {
scanner = new Scanner(System.in);
wu23399_ch07.qxd 12/15/06 19:55 Page 422
scanner.useDelimiter(System.getProperty(line.separator));
bookTracker = new BookTracker();
}
//-------------------------------------------------
// Main Method
//-------------------------------------------------
public static void main(String[] args) {
OverdueChecker checker = new OverdueChecker();
checker.start();
}
//-------------------------------------------------
// Public Methods
//-------------------------------------------------
public void start( ) {
GregorianCalendar returnDate;
String table;
double charge;
Response response;
inputBooks();
table = bookTracker.getList();
System.out.println(table);
System.out.println(nNow check the over due charges...n);
//try different return dates
do {
//read return date
returnDate = readDate(nReturn Date: );
charge = bookTracker.getCharge(returnDate);
displayTotalCharge(charge);
response = prompt(nRun Again (yes/no)? );
} while (response == Response.YES);
System.out.println(
nnThank you for using Library Overdue Checker);
}
//-------------------------------------------------
// Private Methods
//-------------------------------------------------
7.9 Sample Development 423
wu23399_ch07.qxd 12/15/06 19:55 Page 423
7.9 Sample Development—continued
private LibraryBook createBook(String title,
double chargePerDay,
double maxCharge,
GregorianCalendar dueDate) {
if (dueDate == null) {
dueDate = new GregorianCalendar(); //set today as due date
}
LibraryBook book = new LibraryBook(dueDate);
if (title.length()  0) {
book.setTitle(title);
}
if (chargePerDay  0.0) {
book.setChargePerDay(chargePerDay);
}
if (maxCharge  0.0) {
book.setMaximumCharge(maxCharge);
}
return book;
}
private void display(String text) {
System.out.print(text);
}
private void displayTotalCharge(double charge) {
System.out.format(nTOTAL CHARGE:t $%8.2f, charge);
}
private void inputBooks( ) {
double chargePerDay, maxCharge;
String title;
GregorianCalendar dueDate;
LibraryBook book;
//Keeps on reading input from a console
//until stopped by the end user
while (isContinue()) {
System.out.println(n);
title = readString(Title : );
chargePerDay = readDouble(Charge per day: );
424 Chapter 7 Defining Your Own Classes—Part 2
wu23399_ch07.qxd 12/15/06 19:55 Page 424
maxCharge = readDouble(Maximum charge: );
dueDate = readDate (Due Date : );
book = createBook(title, chargePerDay,
maxCharge, dueDate);
bookTracker.add(book);
}
}
private boolean isContinue( ) {
Response response = prompt(nMore books to enter (y/n)?);
return (response == Response.YES);
}
private Response prompt(String question) {
String input;
Response response = Response.NO;
System.out.print(question +  (Yes - y; No - n): );
input = scanner.next();
if (input.equals(Y) || input.equals(y)) {
response = Response.YES;
}
return response;
}
private double readDouble(String prompt) {
display(prompt);
return scanner.nextDouble();
}
private GregorianCalendar readDate( String prompt) {
GregorianCalendar cal;
String yearStr, monthStr, dayStr, line;
int sep1, sep2;
display(prompt);
line = scanner.next();
7.9 Sample Development 425
wu23399_ch07.qxd 12/15/06 19:55 Page 425
7.9 Sample Development—continued
if (line.length() == 0) {
cal = null;
} else {
sep1 = line.indexOf(DATE_SEPARATOR);
sep2 = line.lastIndexOf(DATE_SEPARATOR);
monthStr = line.substring(0, sep1);
dayStr = line.substring(sep1 + 1, sep2);
yearStr = line.substring(sep2 + 1, line.length());
cal = new GregorianCalendar(Integer.parseInt(yearStr),
Integer.parseInt(monthStr)-1,
Integer.parseInt(dayStr));
}
return cal;
}
private String readString(String prompt) {
display(prompt);
return scanner.next();
}
}
426 Chapter 7 Defining Your Own Classes—Part 2
step 3 test Now we run the program multiple times,trying different input types and values.We
also confirm that all control loops are implemented and working correctly. At this point,
the code to compute the overdue charge is still a stub, so we will always get the same
overdue charge for the same number of books.After we verify that everything is working
as expected,we proceed to the next step.
Step 4 Development: Compute the Overdue Charge
In step 4, we complete the stub method that computes the overdue charge in the
LibraryBook class. We have two GregorianCalendar objects for the due date and the
return date.We first need to find out the number of days between the two.We then mul-
tiply this number by the amount of charge per day to derive the total overdue charge. If
this amount is more than the maximum possible charge, then the total charge is reset to
this maximum value.Also,we need to check for the situation in which the return date has
not passed the due date.The logic of this process is a simple computation once we find
out the number of days between the two dates.So,how can we find it?
step 4
design
wu23399_ch07.qxd 12/15/06 19:55 Page 426
Reviewing the GregorianCalendar class, we see the get method can be used to
retrieve different pieces of date information, such as year, month, and day. Using the
method, we can get the month, day, and year information for two dates and compare
these values. It may sound easy, but things can get tricky very quickly. Complexity arises
from the facts that not every month has the same number of days and that the number of
days for February can vary from year to year. This approach is doable, but not recom-
mended.
When we explore the class further, we notice there’s another method, namely get-
Time, that returns a Date object.In Chapter 6,we used this Date class to compute the ex-
ecution time of a loop by finding the difference between the start and end times. We
can apply the same technique here. But instead of using the getTime method, we can
actually use the getTimeInMillis method and bypass the Date class altogether. The
getTimeInMillis method returns the time elasped since the epoch to the date in mil-
liseconds.By subtracting this since-the-epoch milliseconds value of the due date from the
same of the return date, we can find the difference between the two. If the difference is
negative, then it’s not past due, so there’s no charge. If the difference is positive, then we
convert the milliseconds to the equivalent number of days and multiply it by the per-day
charge to compute the total charge.Here’s a simple way to do the conversion:
private static final double MILLISEC_TO_DAY
= 1.0 / 1000 / 60 / 60 / 24;
...
dayCnt = millisec * MILLISEC_TO_DAY;
We will adopt the second approach. Here’s the final computeCharge method of
the LibraryBook class:
public double computeCharge(GregorianCalendar returnDate){
double charge = 0.0;
long dueTime = dueDate.getTimeInMillis();
long returnTime = returnDate.getTimeInMillis();
long diff = returnTime - dueTime;
if (diff  0) {
charge = chargePerDay * diff * MILLISEC_TO_DAY;
if (charge  maximumCharge) {
charge = maximumCharge;
}
}
return charge;
}
We run the program mutiple times again,possibly using the same set of input data.
We enter different input variations to try out all possible cases for the computeCharge
7.9 Sample Development 427
step 4 code
design
alternative 1
design al-
ternative 2
step 4 test
wu23399_ch07.qxd 12/15/06 19:55 Page 427
7.9 Sample Development—continued
method.Try cases such as the return date and due date are the same, the return date oc-
curs before the due date, the charge is beyond the maximum, and so forth. After we ver-
ify the program,we move on to the next step.
Step 5 Development:Tying Up the Loose Ends and Future Extensions
As always, we will perform a critical review of the program, looking for any unfinished
method, inconsistency or error in the methods, unclear or missing comments, and so
forth.We should also not forget to improve the program for cleaner code and better read-
ability.This is especially true for the input routines.Are all the possible cases handled? Are
the input routines easy to use? Will it be better if we allow different formats for entering
the date information?
We stated at the beginning of this section that it would be a better program if it
warned the user, say, by popping a warning window or ringing an alarm, when the due
date was approaching. Using this extended program, we enter the book data at the time
we check out the book from the library.The program will store the entered information in
a file, so we don’t have to reenter the same data whenever we want to find out the total
overdue charge. We can execute the program daily and be warned about the looming
due dates. We can still run the program to find out the charges for the overdue books.
Techniques necessary to implement such an extended program are covered in the later
chapters of this book.
428 Chapter 7 Defining Your Own Classes—Part 2
program
review
possible
extensions
S u m m a r y
• When a method returns an object, it is actually returning a reference to this
object.
• The reserved word this is used to refer to a receiving object of a message
from within this object’s method.
• A class may include multiple methods with the same name as long as their
signatures are different. The signature of a method refers to the name of the
method and the number and data types of its parameters. They are called
overloaded methods.
• A class may include multiple constructors as long as their signatures are
different. They are called overloaded constructors.
• A constructor can call another constructor of the same class using the
reserved word this.
• Class variables and class methods are declared by using the reserved word
static.
• Class methods can access only the class variables and the class constants.
wu23399_ch07.qxd 12/15/06 19:55 Page 428
• Instance methods can access all types of data members (i.e., both class and
instance components).
• Arguments are passed to the methods by using the call-by-value scheme in
which the value of an argument is passed. The value is the actual data in the
case of a primitive data type and a reference to an object in the case of a
reference data type.
• Programmer-defined classes can be grouped into a programmer-defined
package.
• The javadoc comment is the third style of comments used in Java. From the
javadoc comments in a class, a tool can generate its documentation in the
HTML format.
Exercises 429
K e y C o n c e p t s
E x e r c i s e s
returning objects from methods
self referencing pointer (this)
overloaded methods
method signatures
multiple constructors
copy constructors
static initializers
call-by-value scheme
programmer-defined packages
javadoc comments
1. Consider the following classes.
class Cat {
private String name;
private Breed breed;
private double weight;
public Cat(String name, Breed breed, double weight){
this.name = name;
this.breed = breed;
this.weight = weight;
}
public Breed getBreed() {
return breed;
}
public double getWeight() {
return weight;
}
//other accessors and mutators
. . .
}
wu23399_ch07.qxd 12/15/06 19:55 Page 429
class Breed {
private String name;
private double averageWgt; //in lbs.
public Breed(String name, double averageWgt){
this.name = name;
this.averageWgt = averageWgt;
}
public double getWeight( ) {
return averageWgt;
}
//other accessors and mutators
. . .
}
Identify the invalid statements in the following main class. For each invalid
statement, state why it is invalid.
class Q1Main {
public static void main(String[] args ) {
Breed persian = new Breed(Persian, 10.0);
Cat chacha = new Cat(Cha Cha, persian, 12.0);
Cat bombom = new Cat(Bom Bom, mix, 10.0);
Cat puffpuff = new Cat(Puff Puff, chacha, 9.0);
double diff = chacha.getWeight()
- persian.getWeight();
System.out.println(
puffpuff.getBreed().getWeight());
}
}
2. Given the Cat and Breed classes from Exercise 1, what will be the output
from the following code?
class Q2Main {
public static void main(String[] args) {
Cat myCat = new Cat(winky,
new Breed(mix, 10.5), 9.5);
System.out.println(myCat.getWeight());
System.out.println(myCat.getBreed().getWeight());
}
}
430 Chapter 7 Defining Your Own Classes—Part 2
wu23399_ch07.qxd 12/15/06 19:55 Page 430
3. Given the Fraction class from Section 7.8, draw the state-of-memory
diagram at the point immediately after the last statement is executed.
Fraction f1, f2, f3;
f1 = new Fraction(3, 8);
f2 = new Fraction(2, 3);
f3 = f1.add(f2);
4. Consider the following class.
class Dog {
. . .
private double weight;
. . .
public boolean isBiggerThan(Dog buddy) {
return this.getWeight()  buddy.getWeight();
}
public double getWeight() {
return weight;
}
. . .
}
For each of the following codes, complete the state-of-memory diagram by
filling in the arrows for this and buddy.
a. Dog tuffy = new Dog(...);
Dog puffy = new Dog(...);
puffy.isBiggerThan(tuffy);
tuffy
puffy
:Dog :Dog
this
buddy
Exercises 431
wu23399_ch07.qxd 12/15/06 19:55 Page 431
b. Dog tuffy = new Dog(...);
Dog puffy = new Dog(...);
tuffy.isBiggerThan(puffy);
5. Complete the following constructor.
class Student {
private String name;
private int age;
private Address address;
public Student(String name, int age, Address address){
//assign passed values to the data members
}
6. Which of the following groups of overloaded constructors are valid?
a. public Cat(int age) { ... }
public Cat(double wgt) { ... }
b. public Dog(String name, double weight) { ... }
public Dog(String name, double height) { ... }
c. public Dog(String name, double weight) { ... }
public Dog(double weight, String name) { ... }
d. public Cat(String name) { ... }
public Cat(String name, double weight) { ... }
public Cat(double weight) { ... }
7. Which of the following groups of overloaded methods are valid?
a. public void compute(int num) { ... }
public int compute(double num) { ... }
b. public void move(double length) { ... }
public void move( ) { ... }
tuffy
puffy
:Dog :Dog
this
buddy
432 Chapter 7 Defining Your Own Classes—Part 2
wu23399_ch07.qxd 12/15/06 19:55 Page 432
c. public int adjust(double amount) { ... }
public void adjust(double amount, double charge) { ... }
d. public void doWork( ) { ... }
public void doWork(String name) { ... }
public int doWork(double num) { ... }
8. Complete the first four constructors of the following class. Each of the four
constructors calls the fifth one by using the reserved word this.
class Cat {
private static final String DEFAULT_NAME = No name;
private static final int DEFAULT_HGT = 6;
private static final double DEFAULT_WGT = 10.0;
private String name;
private int height;
private double weight;
public Cat( ) {
//assign defaults to all data members
}
public Cat(String name) {
//assign the passed name to the data member
//use defaults for height and weight
}
public Cat(String name, int height) {
//assign passed values to name and height
//use default for weight
}
public Cat(String name, double weight) {
//assign passed values to name and weight
//use default for height
}
public Cat(String name, int height, double weight){
this.name = name;
this.height = height;
this.weight = weight;
}
...
}
9. Define a class method (static) named compare to the Fraction class. The
compare method accepts two Fraction objects f1 and f2. The method returns
-1 if f1 is less than f2
0 if f1 is equal to f2
+1 if f1 is greater than f2
Exercises 433
wu23399_ch07.qxd 12/15/06 19:55 Page 433
10. Rewrite the compare method from Exercise 9 by changing it to an instance
method. This method accepts a Fraction object and compares it to the
receiving object. The method is declared as follows:
public int compare(Fraction frac) {
//compare the Fraction objects this and frac
//return the result of comparison
}
11. Discuss the pros and cons of the compare methods from Exercise 8 and
Exercise 9.
12. Consider the following class.
class Modifier {
public static change(int x, int y){
x = x - 10;
y = y + 10;
}
}
What will be an output from the following code?
int x = 40;
int y = 20;
Modifier.change(x,y);
System.out.println(x =  + x);
System.out.println(y =  + y);
13. Modify the following class to make it a part of the package named myutil. In
addition to adjusting the source file, what are the steps you need to take so
that the class becomes usable/accessible from other classes that are outside
of this myutil package?
class Person {
private String name;
public Person( ) {
name = Unknown;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
14. (Optional) Although we have not discussed the internal workings of the
BookTracker class, it is not too difficult to realize the portion that handles the
434 Chapter 7 Defining Your Own Classes—Part 2
wu23399_ch07.qxd 12/15/06 19:55 Page 434
generation of book list. Define a new method called getListWithCharge
based on the getList method. Generate a book list as the getList method does,
but include the overdue charge for each book also.
15. Design a class that keeps track of a student’s food purchases at the campus
cafeteria. A meal card is assigned to an individual student. When a meal card
is first issued, the balance is set to the number of points. If the student does
not specify the number of points, then the initial balance is set to 100 points.
Points assigned to each food item are a whole number. A student can
purchase additional points at any time during a semester. Every time food
items are bought, points are deducted from the balance. If the balance
becomes negative, the purchase of food items is not allowed. There is
obviously more than one way to implement the MealCard class. Any design
that supports the key functionalities is acceptable.
Development Exercises
For the following exercises, use the incremental development methodology to
implement the program. For each exercise, identify the program tasks, create a
design document with class descriptions, and draw the program diagram. Map out
the development steps at the start. Present any design alternatives and justify your
selection. Be sure to perform adequate testing at the end of each development step.
16. Write an application that plays the game of Fermi. Generate three distinct
random digits between 0 and 9. These digits are assigned to positions 1, 2,
and 3. The goal of the game is for the player to guess the digits in three
positions correctly in the least number of tries. For each guess, the player
provides three digits for positions 1, 2, and 3. The program replies with a
hint consisting of Fermi, Pico, or Nano. If the digit guessed for a given
position is correct, then the reply is Fermi. If the digit guessed for a given
position is in a different position, the reply is Pico. If the digit guessed for a
given position does not match any of the three digits, then the reply is Nano.
Here are sample replies for the three secret digits 6, 5, and 8 at positions 1,
2, and 3, respectively:
Guess Hint Explanation
1 2 5 Nano Nano Pico The value 5 matches but
at the wrong position.
8 5 3 Pico Fermi Nano The value 5 matches at
the correct position. The
value 8 matches but at
the wrong position.
5 8 6 Pico Pico Pico All match at the wrong
positions.
Notice that if the hints like the above are given, the player can tell which
number did not match. For example, given the hint for the second guess, we
Exercises 435
wu23399_ch07.qxd 12/15/06 19:55 Page 435
can tell that 3 is not one of the secret numbers. To avoid this, provide hints in
a random order or in alphabetical order (e.g., it will be Fermi Nano Pico
instead of Pico Fermi Nano for the second reply).
Play games repeatedly until the player wants to quit. After each game,
display the number of guesses made.
Use javadoc comments to document the classes you design for this
application.
17. Write an application that teaches children fraction arithmetic. For each
training session, randomly generate 10 questions involving addition,
subtraction, division, and multiplication of two fractions. At the beginning
of each session, the user has the option of specifying the time limit for
answering the questions. If the time limit is not specified, then use 30 s as a
default time limit. After you pose a question, wait until the user answers the
question. Award points based on the following rules:
Answer Time Points
Correct Under limit 10
Correct Over limit 6
Wrong Under limit 3
Wrong Over limit 0
After one session is over, use the console output to display the grade
distribution and the total points in the following manner:
Under Over
Time Limit Time Limit
Correct Answers 4 3
Wrong Answers 2 1
TOTAL POINTS: 64 (40 + 18 + 6 + 0)
After one session is over, give the user the option to play another session.
436 Chapter 7 Defining Your Own Classes—Part 2
wu23399_ch07.qxd 12/15/06 19:55 Page 436
Exceptions and Assertions
O b j e c t i v e s
After you have read and studied this chapter,you should be able to
• Improve the reliability of code by
incorporating exception-handling and
assertion mechanisms.
• Write methods that propagate exceptions.
• Implement the try-catch blocks for catching
and handling the thrown exceptions.
• Write programmer-defined exception classes.
• Distinguish between the checked and
unchecked,or runtime,exceptions.
• Use assertions in methods to increase the
chance of detecting bugs during the
development.
437
8
wu23399_ch08.qxd 12/15/06 19:57 Page 437
hen someone says his or her program is reliable, what do we expect from the
program? The majority of people would probably reply correctness as the most
important criterion in determining the reliability of a program. When a program is
claimed to be reliable, we certainly expect the program will produce correct results
for all valid input. It is hardly a reliable program if it produces correct results only
for some input values. As we all know by now, writing a correct program is easier
said than done. If we are not diligent and careful enough, we can easily introduce
bugs in our programs. And often we fail to eradicate them. A mechanism called an
assertion can be used to improve the likelihood of catching logical errors during the
development. We will introduce assertions in this chapter and show how to use them
effectively in our programs.
Program correctness guarantees correct results for all valid input. But
what happens when the input is invalid? Another important criterion of program
reliability is the robustness, which measures how well the program runs under
various conditions. If a program crashes too easily when a wrong type of argu-
ment is passed to a method or an invalid input value is entered, we cannot say
the program is very reliable. A mechanism called exception handling can be
used to improve the program’s robustness. In this chapter, we will describe
how to code this exception-handling mechanism in Java to improve the program’s
robustness.
8.1 Catching Exceptions
In Chapters 5 and 6 we presented two types of control flows: selection control and
repetition control. Using these control structures, we alter the default sequential
flow of control. We use a selection control to select and execute one block of code
out of many choices, and we use a repetition control to execute a block of code
repeatedly until certain conditions are met. The exception-handling mechanism
can be viewed as another form of control structure. An exception represents an
error condition that can occur during the normal course of program execution.
When an exception occurs, the normal sequence of flow is terminated and the
exception-handling routine is executed. When an exception occurs, we say an
exception is thrown. When the matching exception-handling code is executed, we say
the thrown exception is caught. By using exception-handling routines judiciously in
our code, we can increase its robustness. In this section, we will show how the
thrown exceptions can be caught and processed.
We have been dealing with exceptions all along. For example, consider this
code:
Scanner scanner = new Scanner(System.in);
System.out.print(Enter integer: );
int number = scanner.nextInt();
438 Chapter 8 Exceptions and Assertions
I n t r o d u c t i o n
W
assertion
exception
handling
exception
wu23399_ch08.qxd 12/15/06 19:57 Page 438
What would happen if we entered, say, abc123, an input value that is not an int? We
would get an error message like this:
Exception in thread main java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:819)
at java.util.Scanner.next(Scanner.java:1431)
at java.util.Scanner.nextInt(Scanner.java:2040)
at java.util.Scanner.nextInt(Scanner.java:2000)
at Ch8Sample1.main(Ch8Sample1.java:35)
This error message indicates the system has caught an exception called the Input-
MismatchException, an error that occurs when we try to convert a string that cannot
be converted to a numerical value. Up until now, we have let the system handle
the thrown exceptions. However, when we let the system handle the exceptions,
a single thrown exception most likely will result in erroneous results or a program
termination. Instead of depending on the system for exception handling, we can
increase the program’s reliability and robustness if we catch the exceptions our-
selves by including error recovery routines in our program.
Let’s begin with a short program to illustrate the exception-handling
mechanism. We will define a service class that supports a method to input a
person’s age. This class is mainly for the illustrative purpose of introducing the
exceptionhandling concept.We first define it without exception handling and then
improve it gradually by adding exception-handling features. Because we will be
defining many different versions of the class, we will name them AgeInputVer1,
AgeInputVer2, and so forth. Here’s the AgeInputVer1 class without exception
handling:
8.1 Catching Exceptions 439
/*
Chapter 8 Sample Class: Class to input age
File: AgeInputVer1.java
*/
import java.util.*;
class AgeInputVer1 {
private static final String DEFAULT_MESSAGE = Your age: ;
private Scanner scanner;
public AgeInputVer1( ) {
scanner = new Scanner(System.in);
}
public int getAge() {
return getAge(DEFAULT_MESSAGE);
}
wu23399_ch08.qxd 12/15/06 19:57 Page 439
Using this service class, we can write a program that gets a person’s age and
replies with the year in which the person was born. Notice the program takes into con-
sideration whether the person already had a birthday this year. Here’s the program:
440 Chapter 8 Exceptions and Assertions
public int getAge(String prompt) {
System.out.print(prompt);
int age = scanner.nextInt();
return age;
}
}
/*
Chapter 8 Sample Program: Input a person's age
File: Ch8AgeInputMain.java
*/
import java.util.*;
class Ch8AgeInputMain {
public static void main(String[] args) {
GregorianCalendar today;
int age, thisYear, bornYr;
String answer;
Scanner scanner = new Scanner(System.in);
AgeInputVer1 input = new AgeInputVer1( );
age = input.getAge(How old are you? );
today = new GregorianCalendar( );
thisYear = today.get(Calendar.YEAR);
bornYr = thisYear - age;
System.out.print(Already had your birthday this year? (Y or N));
answer = scanner.next();
if (answer.equals(N) || answer.equals(n) ) {
bornYr--;
}
System.out.println(nYou are born in  + bornYr);
}
}
wu23399_ch08.qxd 12/15/06 19:57 Page 440
The program works fine as long as valid input is entered. But what happens if
the user spells out the age, say, nine instead of 9? An input mismatch exception is
thrown because the input value nine cannot be converted to an integer by using the
parseInt method. With the current implementation, the system will handle the
thrown exception by displaying the error message
Exception in thread main java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:819)
at java.util.Scanner.next(Scanner.java:1431)
at java.util.Scanner.nextInt(Scanner.java:2040)
at java.util.Scanner.nextInt(Scanner.java:2000)
at AgeInputVer1.getAge(AgeInputVer1.java:48)
at Ch8AgeInputMain.main(Ch8AgeInputMain.java:30)
and terminating the program. It would be a much better program if we could handle
the thrown exception ourselves. Let’s modify the getAge method so that it will loop
until a valid input that can be converted to an integer is entered. To do this, we need
to wrap the statements that can potentially throw an exception with the try-catch
control statement. In this example, there’s only one statement that can potentially
throw an exception, namely,
age = scanner.nextInt();
We put this statement inside the try block and the statements we want to be executed
in response to the thrown exception in the matching catch block. If we just want
to display an error message when the exception is thrown, then we can write the
try-catch statement as follows:
System.out.print(prompt);
try {
age = scanner.nextInt();
} catch (InputMismatchException e) {
System.out.println(
Invalid Entry. Please enter digits only.);
}
Statements in the try block are executed in sequence. When one of the state-
ments throws an exception, then control is passed to the matching catch block and
statements inside the catch block are executed. The execution next continues to the
statement that follows this try block statement, ignoring any remaining statements
in the try block. If no statements in the try block throw an exception, then the catch
block is ignored and execution continues with the statement that follows this
8.1 Catching Exceptions 441
try-catch
A statement that
could throw an
exception
The type of exception
to be caught
wu23399_ch08.qxd 12/15/06 19:57 Page 441
try-catch statement. Figure 8.1 shows the two possible control flows: one when an
exception is thrown and another when no exceptions are thrown.
In the sample code, we have only one statement in the try block. If the input
statement does not throw an exception, then we want to exit from the method and
return the integer. If there’s an exception, we display an error message inside the
catch block, and repeat the input routine. To accomplish this repetition, we will put
the whole try-catch statement inside a loop:
public int getAge(String prompt) {
int age;
boolean keepGoing = true;
while (keepGoing) {
System.out.print(prompt);
try {
age = scanner.nextInt();
keepGoing = false;
} catch (InputMismatchException e) {
scanner.next(); //remove the leftover garbage
//from the input buffer
System.out.println(
442 Chapter 8 Exceptions and Assertions
Assume t–stmt–3 throws an exception.
Exception
try {
t–stmt–1
t–stmt–2
t–stmt–3
t–stmt–4
...
t-stmt-n
} catch (Exception e) {
c–stmt–1
...
c–stmt–n
}
next stmt
This part is
skipped.
No exception
try {
t–stmt–1
t–stmt–2
t–stmt–3
t–stmt–4
...
t–stmt n
} catch (Exception e) {
c–stmt–1
...
c–stmt–n
}
next stmt
Figure 8.1 Two possible control flows of the try-catch statement with one catch block.Assume t-stmt-3
throws an exception.
This statement is executed
only if no exception
is thrown.
This will remove
“garbage”left in the
input buffer.
wu23399_ch08.qxd 12/15/06 19:57 Page 442
Invalid Entry.Please enter digits only.);
}
}
return age;
}
Notice the first statement
scanner.next();
inside the catch block. It is used to remove any data that remain in the input buffer.
When an exception is thrown, an input value that has caused an exception still
remains in the input buffer. We need to remove this “garbage” from the input buffer,
so we can process the next input value. If we don’t include this statement, the code
will result in an infinite loop because the nextInt method continues to process the
same invalid input.
We can get rid of the boolean variable by rewriting the statement as
while (true) {
System.out.print(prompt);
try {
age = scanner.nextInt();
return age;
} catch (InputMismatchException e) {
scanner.next(); //remove the leftover garbage
//from the input buffer
System.out.println(
Invalid Entry. Please enter digits only.);
}
}
The improved class with the exception-handling getAge method is named
AgeInputVer2.
There are many types of exceptions the system can throw, and we must specify
which exception we are catching in the catch block’s parameter list (there can be
exactly one exception in the list). In the sample code, we are catching the input
mismatch exception, and the parameter e represents an instance of the InputMis-
matchException class. In Java an exception is represented as an instance of the
Throwable class or its subclasses. The Throwable class has two subclasses, Error and
Exception. The Error class and its subclasses represent serious problems that should
not be caught by ordinary applications, while the Exception class and its subclasses
represent error conditions that should be caught. So for all practical purposes, we are
only interested in the Exception class and its subclasses in our program. Later in the
chapter we will learn how to define our own exception classes. We will declare these
programmer-defined exception classes as subclasses of the Exception class.
8.1 Catching Exceptions 443
wu23399_ch08.qxd 12/15/06 19:57 Page 443
There are two methods defined in the Throwable class that we can call to get
some information about the thrown exception: getMessage and printStackTrace. We
can call these methods inside the catch block as follows:
try {
age = scanner.nextInt();
return age;
} catch (InputMismatchException e) {
scanner.next (); //remove the leftover garbage
//from the input buffer
System.out.println(e.getMessage());
e.printStackTrace();
}
With this modified code, if we enter ten as an input, then we will receive the
following output:
null
java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:819)
at java.util.Scanner.next(Scanner.java:1431)
at java.util.Scanner.nextInt(Scanner.java:2040)
at java.util.Scanner.nextInt(Scanner.java:2000)
at AgeInputVer2.getAge(AgeInputVer2.java:54)
at Ch8AgeInputMain.main(Ch8AgeInputMain.java:30)
Notice that the result we see from the printStackTrace method is the one we saw when
the system handled the thrown exception. The stack trace shows the sequence of calls
made from the main method of the main class to the method that throws the exception.
444 Chapter 8 Exceptions and Assertions
getMessage
printStackTrace
1. What will be displayed on the console window when the following code is
executed and the user enters abc123 and 14?
Scanner scanner = new Scanner(System.in);
try {
int num1 = scanner.nextInt();
System.out.println(Input 1 accepted);
int num2 = scanner.nextInt();
System.out.println(Input 2 accepted);
} catch (InputMismatchException e) {
System.out.println(Invalid Entry);
}
wu23399_ch08.qxd 12/15/06 19:57 Page 444
2. What is wrong with the following code? It attempts to loop until the valid input
is entered.
Scanner scanner = new Scanner(System.in);
try {
while (true) {
System.out.print(Enter input: );
int num = scanner.nextInt();
}
} catch (InputMismatchException e) {
scanner.next();
System.out.println(Invalid Entry);
}
8.2 Throwing Exceptions and Multiple catch Blocks
Compared to the original AgeInputVer1 class, the AgeInputVer2 class is more robust
because the program does not terminate abruptly when an invalid value is entered.
However, the improved class is not robust enough yet. There is still room for im-
provements. For example, the current implementation accepts invalid negative inte-
gers. Since negative age is not possible, let’s improve the code by disallowing the input
of negative integers. Notice that a negative integer is an integer, so the nextInt method
will not throw an exception. We will define the third class, AgeInputVer3, to throw
(and catch) an exception when the invalid input of a negative integer is detected.
Here’s the while loop of the modified getAge method of the AgeInputVer3 class:
while (true) {
System.out.print(prompt);
try {
age = scanner.nextInt();
if (age  0) {
throw new Exception(Negative age is invalid);
}
return age; //input okay so return the value  exit
} catch (InputMismatchException e) {
scanner.next();
System.out.println(Input is invalid.n +
Please enter digits only);
} catch (Exception e) {
System.out.println(Error:  + e.getMessage());
}
}
8.2 Throwing Exceptions and Multiple catch Blocks 445
Throws an excep-
tion when age is a
negative integer.
The thrown exception
is caught by this
catch block.
wu23399_ch08.qxd 12/15/06 19:57 Page 445
An exception is thrown by using the throw statement. Its syntax is
throw a throwable object
where a throwable object is an instance of the Throwable class or its subclasses.
As mentioned earlier, in common applications, it will be an instance of the Exception
class or its subclasses. In the sample code, we threw an instance of the Exception
class. When we create an instance of the Exception class, we can pass the string that
describes the error. The thrown exception is caught by the corresponding catch
block, and this error message is displayed.
Notice the multiple catch blocks in the sample code. When there are multi-
ple catch blocks in a try-catch statement, they are checked in sequence, and
because the exception classes form an inheritance hierarchy, it is important to check
the more specialized exception classes before the more general exception classes.
For example, if we reverse the order of the catch blocks to
try {
...
} catch (Exception e) {
...
} catch (InputMismatchException e) {
...
}
then the second catch block will never be executed because any exception object
that is an instance of Exception or its subclasses will match the first catch block.
When an exception is thrown, a matching catch block is executed and all other catch
blocks are ignored. This is similar to the switch statement with break at the end of
each case block. The execution continues to the next statement that follows the
trycatch statement. When no exception is thrown, all catch blocks are ignored and
the execution continues to the next statement. Figure 8.2 illustrates the control flow
of the try-catch statement with multiple catch blocks.
446 Chapter 8 Exceptions and Assertions
List the catch blocks in the order of specialized to more general exception classes.
At most one catch block is executed,and all other catch blocks are ignored.
The sample code given at the beginning of this section illustrates how an ex-
ception can be thrown and caught by the matching catch block. Instead of catching the
thrown exception immediately, it is possible to let others handle the exception. This
can be achieved by not including a matching catch block. We assume in Figure 8.2
that one of the catch blocks will match the thrown exception, but it is not a require-
ment. It is possible that none of the catch blocks matches the thrown exception.
wu23399_ch08.qxd 12/15/06 19:57 Page 446
If there is no matching catch block, then the system will search down the stack trace
for a method with a matching catch block. If none is found, then the system will
handle the thrown exception. We will explain this traversing of the stack trace in
greater detail in Section 8.3.
If there is a block of code that needs to be executed regardless of whether an
exception is thrown, then we use the reserved word finally. Consider this code.
try {
num = scanner.nextInt();
if (num  100) {
throw new Exception(Out of bound);
}
} catch (InputMismatchException e) {
scanner.next();
System.out.println(Not an integer);
} catch (Exception e) {
System.out.println(Error: + e.getMessage());
} finally {
System.out.println(DONE);
}
8.2 Throwing Exceptions and Multiple catch Blocks 447
Assume t–stmt–3 throws an exception and
catch–block–3 is the matching catch block.
Exception
try {
t–stmt–1
t–stmt–2
t–stmt–3
t–stmt–4
...
t–stmt–n
}
catch–block–1
catch–block–2
catch–block–3
catch–block–4
...
catch–block–n
next stmt
try {
t–stmt–1
t–stmt–2
t–stmt–3
t–stmt–4
...
t–stmt–n
}
catch–block–1
catch–block–2
catch–block–3
catch–block–4
...
catch–block–n
next stmt
No exception
Skipped portion
Figure 8.2 Two possible control flows of the try-catch statement with multiple catch blocks.Assume
t-stmt-3 throws an exception and catch-block-3 is the matching catch block.
wu23399_ch08.qxd 12/15/06 19:57 Page 447
If there is no error in input, then no exception is thrown and the output will be
DONE
If there is an error in input, one of the two exceptions is thrown and the output
will be
Not an integer
DONE
or
Error: Out of bound
DONE
The example shows that the finally block is always executed. This feature is use-
ful in a situation where we need to execute some cleanup code after the try-catch state-
ment. For example, suppose we open a communication channel from our Java program
to a remote Web server to exchange data. If the data exchange is successfully com-
pleted in the try block, then we close the communication channel and finish the opera-
tion. If the data exchange is interrupted for some reason, an exception is thrown and the
operation is aborted. In this case also, we need to close the communication channel,
because leaving the channel open by one application blocks other applications from
using it. Closing a channel is much like hanging up the phone. The code to close the
communication channel should therefore be placed in the finally block. Figure 8.3
shows two possible control flows for the try-catch statement with the finally clause.
448 Chapter 8 Exceptions and Assertions
Assume t–stmt–i throws an exception and
catch–block–i is the matching catch block.
Exception
try {
t–stmt–1
...
t–stmt–i
...
t–stmt–n
}
catch–block–1
...
catch–block–i
...
catch–block–n
finally {
...
}
next statement
try {
t–stmt–1
...
t–stmt–i
...
t–stmt–n
}
catch–block–1
...
catch–block–i
...
catch–block–n
finally {
...
}
next statement
No exception
Skipped portion
Figure 8.3 Two possible control flows of the try-catch statement with multiple catch blocks and the finally
block.The finally block is always executed.
wu23399_ch08.qxd 12/15/06 19:57 Page 448
Note that even if there’s a return statement inside the try block, the finally
block is executed. When the return statement is encountered in the try block,
statements in the finally block are executed before actually returning from the
method.
8.2 Throwing Exceptions and Multiple catch Blocks 449
1. What’s wrong with the following code? Identify all errors.
Scanner scanner = new Scanner(System.in);
try {
int num = scanner.nextInt();
if (num  100) {
catch new Exception(Out of bound);
}
} catch (InputMismatchException e) {
System.out.println(Invalid Entry);
} finally(Exception e) {
System.out.println(DONE);
}
2. Determine the output of the following code when the input a12 is entered.
Scanner scanner = new Scanner(System.in);
try {
int num = scanner.nextInt();
if (num  0) {
throw new Exception(No negative);
}
} catch (InputMismatchException e) {
System.out.println(Invalid Entry);
} catch (Exception e) {
System.out.println(Error: + e.getMessage());
} finally {
System.out.println(DONE);
}
wu23399_ch08.qxd 12/15/06 19:57 Page 449
3. Determine the output of the following code when the input a12 is entered.
Scanner scanner = new Scanner(System.in);
try {
int num = scanner.nextInt();
if (num  0) {
throw new Exception(No negative);
}
} catch (Exception e) {
System.out.println(Error: + e.getMessage());
} catch (InputMismatchException e) {
System.out.println(Invalid Entry);
}
8.3 Propagating Exceptions
In Section 8.2 we introduced the possibility of no catch block matching the thrown
exception, but we did not explain exactly how the system handles such a case. We
stated only briefly that the system will search down the stack trace for a method
with a matching catch block, and if no matching catch block is found, the system
will handle the thrown exception. We now describe this mechanism in detail.
To present a precise description, we start with some definitions. When a
method may throw an exception, either directly by including a throw statement or
indirectly by calling a method that throws an exception, we call the method an
exception thrower. Every exception thrower must be one of the two types: catcher or
propagator. An exception catcher is an exception thrower that includes a matching
catch block for the thrown exception, while an exception propagator does not. For
example, the getAge method of the AgeInputVer1 class is an exception propagator,
while the getAge method of the AgeInputVer2 class is an exception catcher. Note
that the designation of a method as being a catcher or propagator is based on a sin-
gle exception. Suppose a method throws two exceptions. This method can be a
catcher of the first exception and a propagator of the second exception.
Let’s consider the sequence of method calls shown in Figure 8.4. Method A
calls method B, method B in turn calls method C, and so forth. Notice the stack
trace in the figure. Every time a method is executed, the method’s name is placed
on top of the stack. By the time method D is executed, we have A, B, C, and D in
the stack. When an exception is thrown, the system searches down the stack from
the top, looking for the first matching exception catcher. Method D throws an
exception, but no matching catch block exists in the method, so method D is
an exception propagator. The system then checks method C. This method is also an
exception propagator. Finally, the system locates the matching catch block in
method B, and therefore, method B is the catcher for the exception thrown by
method D.
450 Chapter 8 Exceptions and Assertions
exception
thrower
exception
catcher
exception
propagator
wu23399_ch08.qxd 12/15/06 19:57 Page 450
Method A also includes the matching catch block, but it will not be executed
because the thrown exception is already caught by method B, and method B does
not propagate this exception. Although the technique is not used often, an exception
catcher can also be set to propagate the caught exception. For example, if we rewrite
method B as
try {
C();
} catch (Exception e) {
... //do something here
throw e; //propagate the caught exception to the
//method below this one in the trace stack
}
it is both a catcher and a propagator. With the modified method B, method A’s
matching catch block will get executed, because method B, in addition to handling
the exception, throws the same exception, causing the system to look for a match-
ing catcher down the stack.
We have one last detail to complete the description of the exception propaga-
tion mechanism. If a method is an exception propagator, we need to modify its
8.3 Propagating Exceptions 451
Call sequence
Stack trace
Method A
Catcher Propagator Propagator
try {
B();
}
catch (Exception e) {
output.println(A);
}
Method B
try {
C();
}
catch (Exception e) {
output.println(B);
}
Method C
D();
Method D
Method A Method B
D
C
B
A
C
B
A
B
A
Method C Method D
if (cond) {
throw
new Exception();
}
A
Figure 8.4 A sequence of method calls among the exception throwers.Method D throws an instance of
Exception. The green arrows indicate the direction of calls.The red arrows show the reversing of call sequence,
looking for a matching catcher.Method B is the catcher in this example.The call sequence is traced by using a
stack.(Note: output == System.out.)
wu23399_ch08.qxd 12/15/06 19:57 Page 451
header to declare the type of exceptions the method propagates. We use the reserved
word throws for this declaration. Methods C and D in Figure 8.4 must have the fol-
lowing declaration (visibility modifier and return type are not relevant here):
void C( ) throws Exception {
...
}
void D( ) throws Exception {
...
}
Without the required throws Exception clause, the program will not compile.
There is one exception (no pun intended) to this rule. For the exceptions of the
type called runtime exceptions, the throws clause is optional. For example,
the getAge method of AgeInputVer1 does not include the throws clause be-
cause InputMismatchException is a runtime exception. Its being optional means
we can include it to explicitly state the fact if we want to. If we restate the
declaration to
public int getAge(String prompt)
throws InputMismatchException {
...
}
the code will compile just fine. We will explain further about different types of
exceptions in Section 8.4.
Now that the exception propagation mechanism is explained, let’s study how
we can apply it in designing useful service classes.
First, consider the Fraction class from Chapter 7. The setDenominator method
of the Fraction class was defined as follows:
public void setDenominator(int denom) {
if (denom == 0) {
System.out.println(Fatal Error);
System.exit(1);
}
denominator = denom;
}
We stated in Chapter 7 that it is too drastic to terminate a whole program when one
attempts (inadvertently or otherwise) to set the denomintor to 0. Throwing an ex-
ception is a much better approach. Here’s the modified method that throws an
IllegalArgumentException when the value of 0 is passed as an argument:
public void setDenominator(int denom)
throws IllegalArgumentException {
452 Chapter 8 Exceptions and Assertions
wu23399_ch08.qxd 12/15/06 19:57 Page 452
if (denom == 0) {
throw new IllegalArgumentException(
Denominator cannot be 0);
}
denominator = denom;
}
Now let’s study another example. Consider the AgeInputVer3 class. It dis-
allows input of negative integers. When that happens, an exception is thrown.
Instead of disallowing only negative integers, wouldn’t it make more sense to
restrict the valid input by specifying the lower and upper bounds? For example, we
may want to restrict the input to an integer between 10 and 20 for one application
and between 0 and 250 (e.g., entering the age of a building on the campus) for
another application. To illustrate this concept, we will define the fourth class,
AgeInputVer4, that allows the client programmers to specify the lower and upper
bounds of acceptable input values.
The client specifies the lower and upper bounds at the time of object creation,
for example,
AgeInputVer4 input = new AgeInputVer4(10, 20);
This constructor will set the lower and upper bounds to 0 and 99, respectively. The
lower and upper bounds are kept as data members lowerBound and upperBound,
respectively, and they are initialized in the constructor.
How should the getAge respond when it detects the input is outside the
range of the client-designated lower and upper bounds? Instead of catching it,
we will propagate the thrown exception to the caller of this method. Our responsi-
bility as a provider of the service class is to tell the client by throwing an excep-
tion when a condition set by the client is violated. We will let the client handle
the thrown exception. The condition is set by the client, so it is more appropriate
for this client to decide what to do in case of an exception. For the number format
exception, the getAge method is still the catcher because this exception is thrown
when a condition not dependent on any one specific client is violated. This
exception is not a client-specific exception, but a generic exception suitably han-
dled by the service class. So the modified getAge method is a propagator of an
Exception (thrown when the bounds set by the client are violated) and a catcher
of an InputMismatchException (thrown when the input is not an integer). Here’s
the method:
public int getAge(String prompt) throws Exception {
int age;
while (true) {
System.out.print(prompt);
try {
age = scanner.nextInt();
8.3 Propagating Exceptions 453
Propagates an
Exception
wu23399_ch08.qxd 12/15/06 19:57 Page 453
if (age  lowerBound || age  upperBound) {
throw new Exception(Input out of bound);
}
return age;
} catch (InputMismatchException e) {
scanner.next();
System.out.println(Input is invalid.n +
Please enter digits only);
}
}
}
454 Chapter 8 Exceptions and Assertions
No catch block for
Exception
Don’t catch an exception that is thrown as a result of violating the condition set
by the client programmer.Instead,propagate the exception back to the client
programmer’s code and let him or her handle it.
The second getAge method that uses a default prompt calls this method, so we
need to rewrite the second getAge method as
public int getAge() throws Exception {
return getAge(DEFAULT_MESSAGE);
}
We have to specify the additional data members and the constructors to com-
plete the AgeInputVer4 class. The new data members are declared as
private static final int DEFAULT_LOWER_BOUND = 0;
private static final int DEFAULT_UPPER_BOUND = 99;
private int lowerBound;
private int upperBound;
What about the constructors? Are the following constructors acceptable?
public AgeInputVer4( ) {
this(DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND);
}
public AgeInputVer4(int low, int high) {
lowerBound = low;
upperBound = high;
scanner = new Scanner(System.in);
}
This call can throw an
Exception so the method
header must include the
correct throws clause.
wu23399_ch08.qxd 12/15/06 19:58 Page 454
Bad Version
Yes, if we didn’t know about exception handling. But now with the knowledge of
exception handling, we can make the class more robust by ensuring that low is less
than or equal to high. If this condition is not met, then we throw an exception. The
IllegalArgumentException class is precisely the class we can use for this situation.
Here’s the more robust constructor:
public AgeInputVer4(int low, int high)
throws IllegalArgumentException {
if (low  high) {
throw new IllegalArgumentException(
Low ( + low + ) was  +
larger than high( + high + ));
} else {
lowerBound = low;
upperBound = high;
scanner = new Scanner(System.in);
}
}
Now, what about the default constructor? Since the default constructor calls
the other two-argument constructor, which can throw an exception, this constructor
must handle the exception. One approach is to propagate the exception by declaring
it as
public AgeInputVer4( ) throws IllegalArgumentException {
this(DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND);
}
This declaration is problematic, however, because when we use the throws clause,
we are announcing that this method can potentially throw an exception. But this
constructor will never throw an exception as long as the class is programmed cor-
rectly. The only time this constructor can throw an exception is when we set the
value for DEFAULT_LOWER_BOUND or DEFAULT_UPPER_BOUND incorrectly. It is an
internal error and must be corrected. Since this constructor should not throw an ex-
ception, we might be tempted to make this constructor an exception catcher as
public AgeInputVer4( ) {
try {
this(DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND);
} catch (IllegalArgumentException e) {
//never happens, so do nothing
}
}
Logically, this is what we want to accomplish. But syntactically, it is an error. Java
requires the call to another constructor using the reserved word this to be the first
8.3 Propagating Exceptions 455
wu23399_ch08.qxd 12/15/06 19:58 Page 455
statement. In the bad version, the try statement is the first statement. To correct this
problem, we can define a private method init as
private void init(int low, int high) {
lowerBound = low;
upperBound = high;
scanner = new scanner(System.in);
}
and write the two constructors as
public AgeInputVer4( ) {
init(DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND);
}
public AgeInputVer4(int low, int high)
throws IllegalArgumentException {
if (low  high) {
throw new IllegalArgumentException(
Low ( + low + ) was  +
larger than high( + high + ));
} else {
init(low, high);
}
}
Here’s the complete AgeInputVer4 class:
456 Chapter 8 Exceptions and Assertions
/*
Chapter 8 Sample Class: Class to input age
File: AgeInputVer4.java
*/
import javax.swing.*;
class AgeInputVer4 {
private static final String DEFAULT_MESSAGE = Your age:;
private static final int DEFAULT_LOWER_BOUND = 0;
private static final int DEFAULT_UPPER_BOUND = 99;
private int lowerBound;
private int upperBound;
private Scanner scanner;
Data members
wu23399_ch08.qxd 12/15/06 19:58 Page 456
public AgeInputVer4( ) {
init(DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND);
}
public AgeInputVer4(int low, int high)
throws IllegalArgumentException {
if (low  high) {
throw new IllegalArgumentException(
Low ( + low + ) was  +
larger than high( + high + ));
} else {
init(low, high);
}
}
public int getAge() throws Exception {
return getAge(DEFAULT_MESSAGE);
}
public int getAge(String prompt) throws Exception {
int age;
while (true) {
System.out.print(prompt);
try {
age = scanner.nextInt();
if (age  lowerBound || age  upperBound) {
throw new Exception(Input out of bound);
}
return age; //input okay so return the value  exit
} catch (InputMismatchException e) {
scanner.next();
System.out.println(Input is invalid.n +
Please enter digits only);
}
}
}
private void init(int low, int high) {
lowerBound = low;
upperBound = high;
scanner = new Scanner(System.in);
}
}
8.3 Propagating Exceptions 457
Constructors
getAge
init
wu23399_ch08.qxd 12/15/06 19:58 Page 457
458 Chapter 8 Exceptions and Assertions
1. What’s wrong with the following code?
public void check(int num) {
if (num  0) {
throw new Exception();
}
}
2. What is the difference between the reserved words throw and throws?
3. What’s wrong with the following code?
public InputMismatchException getData( ) {
Scanner scanner = new Scanner(System.in);
try {
System.out.print(Input: );
int num = scanner.nextInt();
return num;
}
}
8.4 Types of Exceptions
We mentioned briefly in Section 8.1 that all types of thrown errors are instances of
the Throwable class or its subclasses. Serious errors that signal abnormal conditions
are represented by the instances of the Error class or its subclasses. Exceptional
cases that common applications are expected to handle are represented by the
instances of the Exception class or its subclasses. Figure 8.5 shows a very small por-
tion of the inheritance hierarchy rooted in the Throwable class.
There are two types of exceptions: checked and unchecked. A checked
exception is an exception that is checked at compile time. All other exceptions are
unchecked exceptions, also called runtime exceptions, because they are unchecked
at compile time and are detected only at runtime. Trying to divide a number by 0
(ArithmeticException) and trying to convert a string with letters to an integer
(NumberFormatException) are two examples of runtime exceptions.
If a method is a propagator (a method that throws but does not catch an
exception) of checked exceptions, then the method must have the throws clause. If a
method is a propagator of runtime exceptions or errors (instances of Error or its sub-
classes), the throws clause is optional. When we call a method that can throw
checked exceptions, then we must use the try-catch statement and place the call in the
try block, or we must modify our method header to include the appropriate throws
clause. When we call a method that can throw runtime exceptions or errors, then
there’s is no such requirement. We just make a call in our method. Figure 8.6 shows
the valid callers of a method that throws checked exceptions, and Figure 8.7
shows the valid callers of a method that throws runtime, or unchecked, exceptions.
checked and
unchecked
exception
wu23399_ch08.qxd 12/15/06 19:58 Page 458
Enforcing the requirement of explicitly handling runtime exceptions means,
for all methods we write, that they must either have the throws clause in the header
or have the try-catch statement in the body because almost every method we call
from our methods can throw runtime exceptions. This is hardly effective program-
ming so we don’t have to handle runtime exceptions explicitly in the program. For
the errors of type Error and its subclasses, they indicate problems too serious for any
ordinary application to handle, so we are not required to handle them explicitly.
There’s really nothing we can do even if we catch them, so we don’t.
8.4 Types of Exceptions 459
IllegalArgumentException
NumberFormatException
ArithmeticException NullPointerException
RuntimeException
AssertionError
Error
IOException
Exception
Throwable
Figure 8.5 Some classes in the inheritance hierarchy from the Throwable class.There are over 60 classes in
the hierarchy.
Caller A (catcher)
void callerA( ) {
try {
doWork( );
} catch (Exception e) {
...
}
Caller B (propagator)
void callerB( )
throws Exception {
...
doWork( );
...
}
doWork throws Exception
public void doWork
throws Exception {
...
throw new Exception();
...
}
Figure 8.6 Callers of a method that can throw a checked exception must explicitly include the try-catch
statement in the method body or the throws clause in the method header.
wu23399_ch08.qxd 12/15/06 19:58 Page 459
460 Chapter 8 Exceptions and Assertions
Caller A (catcher)
void callerA( ) {
try {
doWork( );
} catch (
RuntimeException e) {
...
}
Caller B (propagator)
void callerB( ) throws
RuntimeException {
...
doWork( );
...
}
Caller C (propagator)
void callerC( ) {
...
doWork( );
...
}
doWork throws RuntimeException
public void doWork {
...
throw new
RuntimeException();
...
}
This is the most common
style for runtime exceptions.
Notice that caller C is a
propagator implicitly.
Figure 8.7 It is optional for the callers of a method that can throw runtime,or unchecked,exceptions to
include the try-catch statement in the method body or the throws clause in the method header.
If a method throws a checked exception,the caller of this method must explicitly in-
clude the try-catch statement or the throws clause in the method header.If a
method throws a runtime,or unchecked,exception,the use of the try-catch
statement or the throws clause is optional.
1. Is this code wrong?
public void check(int num) {
if (num  0) {
throw new IllegalArgumentException();
}
}
2. What is the difference between the checked and unchecked exceptions?
wu23399_ch08.qxd 12/15/06 19:58 Page 460
8.5 Programmer-Defined Exceptions
In the AgeInputVer4 class, the getAge methods throw an instance of the Exception
class. The catch clause of the caller of the getAge method can use the getMessage
method to retrieve the error message or use printStackTrace to display the sequence
of method calls from the main method to the method that threw an exception. But
there’s no way for the client to get any other useful information such as the value
actually entered by the user. Instead of using generic exception classes, we can de-
fine our own exception classes so we can attach useful information to the exception
objects.
Let’s define a class named AgeInputException as a subclass of the Exception
class. To provide useful information to the client, we will define the class so the
instances will carry three pieces of information: lower bound, upper bound, and the
value entered by the user (in addition to the message inherited from the Exception
class). We will define three public methods to access these data. Here’s the class
definition:
8.5 Programmer-Defined Exceptions 461
/*
Chapter 8 Sample Class: Customized Exception Class
File: AgeInputException.java
*/
class AgeInputException extends Exception {
private static final String DEFAULT_MESSAGE = Input out of bounds;
private int lowerBound;
private int upperBound;
private int value;
public AgeInputException(int low, int high, int input) {
this(DEFAULT_MESSAGE, low, high, input);
}
public AgeInputException(String msg,
int low, int high, int input) {
super(msg);
if (low  high) {
throw new IllegalArgumentException();
}
lowerBound = low;
upperBound = high;
value = input;
}
wu23399_ch08.qxd 12/15/06 19:58 Page 461
public int lowerBound() {
return lowerBound;
}
public int upperBound() {
return upperBound;
}
public int value() {
return value;
}
}
462 Chapter 8 Exceptions and Assertions
The new AgeInputVer5 class is essentially the same as the AgeInputVer4 class
except the getAge method of the new class throws an AgeInputException. A sample
main class that uses the AgeInputVer5 is as follows:
/*
Chapter 8 Sample Program: Input a person’s age
File: Ch8TestAgeInputVer5.java
*/
class Ch8TestAgeInputVer5 {
public static void main( String[] args ) {
int entrantAge;
try {
AgeInputVer5 input = new AgeInputVer5(25, 50);
entrantAge = input.getAge(Your Age:);
System.out.println(Input Okay. Age =  + entrantAge);
} catch (AgeInputException e) {
System.out.println(
Error:  + e.value() +  is entered. It is  +
outside the valid range of [ + e.lowerBound() +
,  + e.upperBound() + ]);
}
}
}
e’s methods are called
to get info
wu23399_ch08.qxd 12/15/06 19:58 Page 462
8.6 Assertions
In this section we will describe a Java assertion and explain how to use it effec-
tively in our programs. A Java assertion is a language feature we use to detect
logical errors in a program. We will illustrate the key points with a very simple
class that includes a logical error. Because the sample class is simple, the use of
assertion may not seem so helpful. Keep in mind that this class is for illustrative
purposes only. The real benefit of using the assertion feature becomes obvious when
the program logic gets more complex and the number of classes in the program
increases.
Here’s a bank account class that allows withdrawals and deposits. There’s one
logical error in the class:
class BankAccount {
private double balance;
public BankAccount(double initialBalance) {
balance = initialBalance;
}
8.6 Assertions 463
To provide useful information to the client programmers when an exception occurs,
define a new exception class.Make this customized exception class a subclass of
Exception.
When we create a new customized exception class, we should define it as a
checked exception, and the most logical choice for its superclass is the Exception
class. We should not define the customized exception class as an unchecked
exception. If we did, then the client programmers would have an option of omit-
ting the try-catch statement or the throws clause in their code. This is not a good
idea.The goal of defining a customized exception class is to ensure that the client
programmers handle the thrown exceptions of the customized class explicitly in
their code,to increase the robustness of the whole program.
1. When do we want to define a customized exception class?
2. Should a customized exception class be a checked or unchecked exception?
wu23399_ch08.qxd 12/15/06 19:58 Page 463
public void deposit(double amount) {
double oldBalance = balance;
balance -= amount;
assert balance  oldBalance;
}
public void withdraw(double amount) {
double oldBalance = balance;
balance -= amount;
assert balance  oldBalance;
}
public double getBalance( ) {
return balance;
}
}
Notice the two occurences of the reserved word assert in the class definition.
The syntax for the assert statement is
assert boolean expression ;
where boolean expression represents the condition that must be true if the code
is working correctly. When the statement is executed, the boolean expression is
evaluated. If it results in true, then the normal execution of the program continues.
Otherwise, an AssertionError (subclass of Error) is thrown.
In this example, we want to assert that balance is more than oldBalance when
we deposit and less than oldBalance when we withdraw, so we write
assert balance  oldBalance;
and
assert balance  oldBalance;
at the end of the deposit and withdraw methods, respectively.
Now let’s see what happens when we use this BankAccount class in our pro-
gram. Here’s a simplistic main class to test the BankAccount class:
import javax.swing.*;
class Ch8TestAssertMain {
public static void main(String[] args) {
BankAccount acct = new BankAccount(200);
acct.deposit(25);
System.out.println(
Current Balance:  + acct.getBalance());
}
}
464 Chapter 8 Exceptions and Assertions
Here’s a logical error. We
should add the amount.
If this boolean expression
results in false, then an
AssertionError is thrown.
wu23399_ch08.qxd 12/15/06 19:58 Page 464
To run this program with the assertion feature enabled, we must include the
designation -ea as follows:
java -ea Ch8TestAssertMain
(Note: For most Java IDE, you specify this option in the Preference dialog. Please
consult your Java IDE for details.)
If we do not provide the -ea option, then the program is executed without
checking the assertions. When do we ever want to ignore the assertions we inten-
tionally included in the program? Checking all assertions included in the program
can be quite costly. By having an option of enabling or disabling the assertions, we
can choose to enable the assertions while developing and testing the program and
disable them once the program is fully developed and tested.
8.6 Assertions 465
To run the program with assertions enabled,use
java -ea main class
With the assert statements enabled, executing the Ch8TestAssertMain main
class will result in the following error message:
Exception in thread main java.lang.AssertionError
at BankAccount.deposit(BankAccount.java:13)
at Ch8TestAssertMain.main(Ch8TestAssertMain.java:34)
The error message indicates an AssertionError is thrown at line 13 (the actual line
number would be different if the source code included comments) of the Bank-
Account class, which is the assert statement
assert balance  oldBalance;
We can use the second form of the assert statement to provide a customized
error message. The syntax for the second form is
assert boolean expression  : expression;
where expression represents the value that is passed as an argument to the con-
structor of the AssertionError class. The value serves as the detailed message of a
thrown error. For example, using the second form, we can rewrite the deposit
method as
public void deposit(double amount) {
double oldBalance = balance;
balance -= amount;
wu23399_ch08.qxd 12/15/06 19:58 Page 465
assert balance  oldBalance :
Serious Error -- balance becomes less +
after deposit;
}
With this modified deposit method, the error message will be
Exception in thread main java.lang.AssertionError:
Serious Error -- balance becomes less after deposit
at BankAccount.deposit(BankAccount.java:14)
at Ch8TestAssertMain.main(Ch8TestAssertMain.java:34)
Encountering this error message during the development, we are made aware
of the existence of a bug in the program. Without the assertion feature, we may not
be able to detect the bug until very late in the development, or we may not be able
to detect it at all.
Again, for a small class such as BankAccount, the benefit of using assertions
may not be obvious. However, in designing and building classes that solve diffi-
cult and complex problems, effective use of assertions can be an indispensable aid,
especially when it is combined with a full set of testing. We will be seeing more
examples of assertions (and exceptions, also) in the later sample code.
Types of Assertions
The type of assertion we see in the withdraw and deposit methods is called a
postcondition assertion. This assertion checks for a condition that must be true after
a method is executed. Opposite to the postcondition assertion is a precondition
assertion, a checking of condition that must be true before a method is executed.
The third type of assertion is called a control flow invariant. Consider the following
switch statement. It adds the appropriate fee to the tuition based on whether the
student is a dorm resident or a dorm resident or a commuter.
switch (residenceType) {
case COMMUTER: totalFee = tuition + parkingFee;
break;
case DORM_RESIDENT: totalFee = tuition + roomAndBoard;
break;
}
Now every student must be a dorm resident or a commuter, so if the variable resi-
denceType has a value other than COMMUTER or DORM_RESIDENT, then there’s a
bug somewhere. To detect such bug, we can rewrite the statement as
switch (residenceType) {
case COMMUTER: totalFee = tuition + parkingFee;
break;
466 Chapter 8 Exceptions and Assertions
postcondition
assertion
precondition
assertion
control flow
invariant
wu23399_ch08.qxd 12/15/06 19:58 Page 466
Bad Version
case DORM_RESIDENT: totalFee = tuition + roomAndBoard;
break;
default: assert false:
Value of residenceType  +
is invalid. Value =  +
residenceType;
}
This statement documents the fact that the default case should never be executed
when the program is running correctly. This is called a control flow invariant
because the control must flow invariably to one of the two cases. Alternatively, we
can place an assertion before the switch statement as
assert (residenceType == COMMUTER ||
residenctType == DORM_RESIDENT) :
Value of residenceType is invalid. Value =  +
residenceType;
switch (residenceType) {
case COMMUTER: totalFee = tuition + parkingFee;
break;
case DORM_RESIDENT: totalFee = tuition + roomAndBoard;
break;
}
Differentiating Assertions and Exceptions
Because both the assertion and the exception mechanisms are intended to improve
the program reliability, their use is often mixed up. For example, if we are not
attentive, we could end up using the assertion feature wrongly in places where
exceptionhandling routines should be used. Consider the following case. In defining
the deposit and the withdraw methods, we did not bother to check the value of the
parameter (for the sake of a simplified class definition). The passed amount must
be greater than zero for the methods to work correctly. How shall we include such
testing? One possibility (a wrong approach) is to use the assertion feature as (we
only show the withdraw method).
public void withdraw(double amount) {
assert amount  0;
double oldBalance = balance;
balance -= amount;
assert balance  oldBalance;
}
8.6 Assertions 467
wu23399_ch08.qxd 12/15/06 19:58 Page 467
This is not a correct use of assertions. We should not use the assertion feature
to ensure the validity of an argument. In principle, we use assertions to detect the
internal programming errors, and we use exceptions to notify the client program-
mers of the misuse of our classes. The BankAccount class is intended as a service
class used by many different programs. It is the responsibility of the client pro-
grammers to pass the valid arguments. If they don’t, then we throw an exception to
notify them of the misuse. Another problem is that assertions can be enabled or dis-
abled when the program is run. But the validity checking of the arguments should
never be disabled.
468 Chapter 8 Exceptions and Assertions
Use assertions to detect internal errors.Use exceptions to notify the client program-
mers of the misuse of our class.
The correct way to implement the methods is as follows (only the withdraw
method is shown here):
public void withdraw(double amount)
throws IllegalArgumentException {
if (amount = 0) {
throw new IllegalArgumentException(
Amount must be positive);
}
double oldBalance = balance;
balance -= amount;
assert balance  oldBalance;
}
1. Why is the following code wrong?
public void doWork(int num) {
assert num  0;
total += num;
}
2. Name three types of assertions.
wu23399_ch08.qxd 12/15/06 19:58 Page 468
Sample Development
Keyless Entry System
We will develop a program that simulates a secure keyless entry system for a dormi-
tory. Inside the entrance hall of a dorm, there is an entry system where the dorm resi-
dents must enter their names, room numbers, and passwords. Upon entry of valid data,
the system will unlock the inner door that leads to the dorm’s living quarters.To imple-
ment this program, two helper classes are provided. The Door class simulates unlock-
ing of the inner door.The Dorm class manages resident information. An instance of the
Dorm class is capable of adding and deleting resident information, reading and saving
resident information from and to a file, and retrieving information if given the resi-
dent’s name. We can verify the validity of the entered data by checking them against
the information kept by a Dorm object.
8.7 Sample Development 469
8.7 Sample Development
We can turn our simulation program into a real one by replacing the Door
class with a class that actually controls the door. Java provides a mechanism
called Java Native Interface (JNI) which can be used to embed a link to a low-
level device driver code, so calling the open method actually unlocks the
door.
Problem Statement
Implement a sentry program that asks for three pieces of information: resident’s
name,room number,and a password.A password is any sequence of characters
ranging in length from 4 to 8 and is unique to an individual dorm resident. If
everything matches,then the system unlocks and opens the door.We assume no
two residents have the same name. Use the provided support classes Door and
Dorm.
Overall Plan
To provide a complete system, we actually have to write two separate programs. The
first one is the administrative module for adding, removing, and updating the resident
information.The second is the user module that interacts with the residents. Figure 8.8
shows the program diagrams for the two modules.
In this section, we implement the user module. The administrative module is left
as an exercise. To begin our development effort, we must first find out the capabilities
of the Dorm and Door classes. Also, for us to implement the class correctly, we need the
specification of the Resident class.
wu23399_ch08.qxd 12/15/06 19:58 Page 469
8.7 Sample Development—continued
Resident
The Resident class maintains information on individual dorm residents.We will be deal-
ing with many instances of this class in the program. A password assigned to a resident
must be a sequence of 4 to 8 characters. For this class to work properly with the Dorm
class,the class must include these public methods:
470 Chapter 8 Exceptions and Assertions
Dorm
Door
Resident
User module
Dorm Resident
A helper class
provided to us
A class we
implement
One or more classes
we implement
Administrative
module
Figure 8.8 Program diagrams for the user and administrative modules.Notice the same Dorm and
Resident classes are used in both programs.User and administrative modules will include one or more
classes (at least one is programmer-defined).
Public Methods of Resident
public Resident( )
Default constructor that creates a Resident object with name
=“unassigned”
, room =“000”
, and id =“@13”
.
public Resident(String name, String room, String password)
throws IllegalArgumentException
Creates a Resident object with the passed values.
IllegalArgumentException is thrown when the given password has less
than four or more than eight characters.
wu23399_ch08.qxd 12/15/06 19:58 Page 470
8.7 Sample Development 471
public void setName(String name)
Assigns the name.
public void setPassword(String id)
throws IllegalArgumentException
Assigns the password.IllegalArgumentException is thrown when the
given password has less than four or more than eight characters.
public void setRoom(String room)
Assigns the room.
public String getName( )
Returns the name.
public String getPassWord( )
Returns the password.
public String getRoom( )
Returns the room number.
One important restriction to the Resident class is the requirement for the class to
implement the Serializable interface. Because the Resident objects are saved to a file,
Java requires the class definition to include the phrase implements Serializable as
import java.io.*;
class Resident implements Serializable {
...
}
Details on the significance of the clause implements Serializable will be given
when we discuss the file input and output in Chapter 12.
For any object we need to save to a file,its class definition must include the phrase
implements Serializable.
Dorm
The Dorm class is a helper class provided to us. A Dorm object is capable of managing
a list of Resident objects. It allows the client to add, delete, and retrieve Resident
objects. In addition, it is capable of saving a list to a file or reading a list from a file. By
wu23399_ch08.qxd 12/15/06 19:58 Page 471
8.7 Sample Development—continued
having these file input and output features, our program can work with different lists of
residents much as a word processor can work with different documents (files).The class
definition is as follows:
472 Chapter 8 Exceptions and Assertions
Public Methods of Dorm
public Dorm( )
Default constructor that creates a Dorm object.
public Dorm(String filename)
Creates a Dorm object with the resident list read from the file with the
name filename.Throws FileNotFoundException when the
designated file cannot be found and IOException when the file cannot
be read.
public void openFile(String filename)
Reads the resident list from the designated file.Throws
FileNotFoundException when the designated file cannot be found and
IOException when the file cannot be read.
public void saveFile(String filename)
Saves the resident list to the designated file. Throws IOException when the
file cannot be saved.
public void add(Resident resident)
Adds the resident to the list. Throws IllegalArgumentException
when a resident with the same name already exists in the list.We do not allow
duplicate names.Every resident must have a unique name.
public void delete(String name)
Deletes the designated resident from the list.If no such resident is in the list,
nothing happens.
public Resident getResident(String name)
Returns the Resident object with the given name.Returns null if no
matching Resident is found.
public String getResidentList( )
Returns a list of residents as a String.A line separator is used after each
resident.For each resident,the list contains his or her name,room number,
and password.
Door
The Door class is another helper class. It simulates the opening of the door. In a real
control program, a Door object can have an embedded low-level device driver code,
wu23399_ch08.qxd 12/15/06 19:58 Page 472
so it really opens the door. The class definition is as follows:
8.7 Sample Development 473
Public Methods of Door
public Door( )
Default constructor that creates a new Door object.
public void open()
Opens the door.For this simulator class,it displays a simple message dialog.
Now let’s study the overall design of the program. In addition to the given helper
classes and the Resident class, what other classes should we define for this program?
As the number of classes gets larger, we need to plan the classes carefully. For this
program, we will define a controller class named Ch8EntranceMonitor whose instance
will manage all other objects.We will set this class as the program’s main class.The user
interface of the program is handled by the InputHandler class. Its instance is used to
allow the user to enter his or her name, room number, and password. After the required
input data are entered by the user, a Ch8EntranceMonitor checks the validity of the
input data with help from a service Dorm object. If the Dorm object confirms the input
data, the controller then instructs another service object, an instance of Door, to open
the door. The following is our working design document, and Figure 8.9 is the program
diagram.
overall
design
Ch8EntranceMonitor
InputHandler Dorm
Door
JOptionPane Resident
User module
Figure 8.9 The program diagram for the Ch8EntranceMonitor program. There are three classes in the
user module.
wu23399_ch08.qxd 12/15/06 19:58 Page 473
8.7 Sample Development—continued
We will implement the user module in three major steps:
1. Define the Resident class and explore the Dorm class.Start with a program
skeleton to test the Resident class.
2. Define the user interface InputHandler class.Modify the top-level control class
as necessary.
3. Finalize the code by making improvements and tying up loose ends.
Step 1 Development: Program Skeleton
Our first task is to find out about the given Dorm class. (The Door class is a very simple
simulator class so there’s not much to explore.) To be able to test-run the Dorm class,
we must provide the Resident class, so this will be our first step. The purpose of the
skeleton main class in this step is to verify the operations of the Dorm class.
The specification for the Resident class was given to us,so our task is to implement
it according to the specification.No design work is necessary. When we can interact with
an instance of the Dorm class correctly, it confirms that our implementation of the
Resident class is working. To verify the key operations of the Dorm class, the top-level
supervisor object Ch8EntranceMonitor will open a file and list the contents of the file.
Here’s the Resident class:
474 Chapter 8 Exceptions and Assertions
Design Document: Ch8EntranceMonitor
Class Purpose
Ch8EntranceMonitor The top-level control object manages other objects
in the program.This is an instantiable main class.
Door The given predefined class simulates the opening of
a door.
Dorm The given predefined class maintains a list of
Resident objects.
InputHandler The user interface class is for handling input
routines.
program
classes
develop-
ment steps
step 1
design
step 1 code
/*
Chapter 8 Sample Development: Keyless Entry System.
File: Resident.java
*/
wu23399_ch08.qxd 12/15/06 19:58 Page 474
import java.io.*;
class Resident implements Serializable {
private String name;
private String room;
private String password;
public Resident( ) {
this(unassigned, 000, @13);
}
public Resident(String name, String room, String pwd)
throws IllegalArgumentException {
setName(name);
setRoom(room);
setPassword(pwd);
}
public String getName( ) {
return name;
}
public String getPassword( ) {
return password;
}
public String getRoom( ) {
return room;
}
public void setName(String name) {
this.name = name;
}
public void setPassword(String pwd) {
int length = pwd.length();
if (length  4 || length  8) {
throw new IllegalArgumentException();
} else {
this.password = pwd;
}
}
public void setRoom(String room) {
this.room = room;
}
}
8.7 Sample Development 475
Data members
Constructors
Accessors
Mutators
wu23399_ch08.qxd 12/15/06 19:58 Page 475
8.7 Sample Development—continued
The skeleton main class is defined as follows:
476 Chapter 8 Exceptions and Assertions
/*
Chapter 8 Sample Development: Keyless Entry System. (Step 1)
File: Ch8EntranceMonitor.java
*/
import javax.swing.*;
import java.io.*;
class Ch8EntranceMonitor { //Step 1 main class
private Dorm manager;
private Scanner scanner;
public Ch8EntranceMonitor( ) {
manager = new Dorm();
scanner = new Scanner(System.in);
}
public static void main(String[] args) {
Ch8EntranceMonitor sentry = new Ch8EntranceMonitor();
sentry.start();
}
public void start( ) {
openFile( );
String roster = manager.getResidentList();
System.out.println(roster);
}
private void openFile( ) {
String filename;
while (true) {
System.out.println(File to open ('x' to cancel):);
filename = scanner.next();
if (filename.equals(x)) {//input routine is canceled
System.out.println(Program is canceled.);
System.exit(0);
}
start
openFile
wu23399_ch08.qxd 12/15/06 19:58 Page 476
try {
manager.openFile(filename);
return;
} catch (FileNotFoundException e) {
System.out.println(No such file);
} catch (IOException e) {
System.out.println(Error in reading file);
}
}
}
8.7 Sample Development 477
The purpose of step 1 testing is to verify that the Dorm class is used correctly to
open a file and get the contents of the file.To test it, we need a file that contains the resi-
dent information. A sample test file can be created by executing the following program,
which we can modify to create other test data files.
step 1 test
/*
Chapter 8 Sample Development: Keyless Entry System.
A simple class to create dummy test data.
File: SampleCreateResidentFile.java
*/
import java.util.*;
import java.io.*;
class SampleCreateResidentFile {
public static void main(String[] args)throws IOException {
Resident res;
Dorm manager = new Dorm( );
res = new Resident(john, 1-101, 3457);
manager.add(res);
res = new Resident(java, 1-102, 4588);
manager.add(res);
res = new Resident(jill, 3-232, 8898);
manager.add(res);
wu23399_ch08.qxd 12/15/06 19:58 Page 477
8.7 Sample Development—continued
res = new Resident(jack, 3-232, 8008);
manager.add(res);
Scanner scanner = new Scanner(System.in);
System.out.println(Save to which file:);
String filename = scanner.next();
manager.saveFile(filename);
System.exit(0); //terminate the program
}
}
478 Chapter 8 Exceptions and Assertions
Step 2 Development: Create the User Interface
In the second development step, we will implement the user interface class
InputHandler, whose task is to get three pieces of information. The main controller
Ch8EntranceMonitor will call an InputHandler to get input data. An InputHandler
will then go through a sequence of getting the three pieces of data. Once the data are
entered, Ch8EntranceMonitor will ask the InputHandler for these data. The logic of
Ch8EntranceMonitor can be expressed as follows:
InputHandler input = new InputHandler();
. . .
input.getInput();
String name = input.getName();
String room = input.getRoomNumber();
String pwd = input.getPassword();
Given the input data,we can check for the match as
Dorm manager = new Dorm();
. . .
Resident res = manager.getResident(name);
if (res == null) {
System.out.println(Invalid Entry);
step 2
design
wu23399_ch08.qxd 12/15/06 19:58 Page 478
} else if (res.getName().equals(name) 
res.getRoom().equals(room) 
res.getPassword().equals(password)) {
door.open();
} else {
System.out.println (Invalid Entry);
}
The getInput method of the InputHandler class calls the scanner three times to
get the name, room, and password. Each input is recorded in the corresponding data
member.The accessors, such as getName, will simply return the value of the requested
data member.
We will list first the InputHandler class and then the modified Ch8Entrance-
Monitor class.Here’s the InputHandler class:
8.7 Sample Development 479
step 2 code
/*
Chapter 8 Sample Development: Keyless Entry System
File: InputHandler.java
*/
import java.util.*;
class InputHandler {
private static final String BLANK = ;
private String name;
private String room;
private String pwd;
private Scanner scanner;
public InputHandler( ) {
name = BLANK;
room = BLANK;
pwd = BLANK;
scanner = new Scanner(System.in);
}
public void getInput( ) {
System.out.print(Enter Name:);
name = scanner.next();
System.out.print(Enter Room No.:);
room = scanner.next();
System.out.print(Enter Password:);
pwd = scanner.next();
}
Data members
Constructor
getInput
wu23399_ch08.qxd 12/15/06 19:58 Page 479
8.7 Sample Development—continued
public String getName( ) {
return name;
}
public String getRoom( ) {
return room;
}
public String getPassword( ) {
return pwd;
}
}
480 Chapter 8 Exceptions and Assertions
The main class is now modified to control an InputHandler object and to check en-
tered information as the resident list maintained by a Dorm object. Here’s the step 2
Ch8EntranceMonitor class:
/*
Chapter 8 Sample Development: Keyless Entry System.
File: Ch8EntranceMonitor.java (Step 2)
*/
import java.util.*;
import java.io.*;
class Ch8EntranceMonitor {
private Dorm manager;
private Door door;
private InputHandler input;
private Scanner scanner;
public Ch8EntranceMonitor( ) {
manager = new Dorm();
scanner = new Scanner(System.in);
input = new InputHandler();
door = new Door();
}
Data members
Constructors
Accessors
wu23399_ch08.qxd 12/15/06 19:58 Page 480
public static void main(String[] args) {
Ch8EntranceMonitor sentry = new Ch8EntranceMonitor();
sentry.start();
}
public void start( ) {
openFile( );
String roster = manager.getResidentList(); //TEMP
System.out.println(roster); //TEMP
processInputData();
}
private void openFile( ) {
String filename;
while (true) {
System.out.println(File to open ('x' to cancel):);
filename = scanner.next();
if (filename.equals(x)) {//input routine is canceled
System.out.println(Program is canceled.);
System.exit(0);
}
try {
manager.openFile(filename);
return;
} catch (FileNotFoundException e) {
System.out.println(No such file);
} catch (IOException e) {
System.out.println(Error in reading file);
}
}
}
private void processInputData( ) {
String name, room, pwd;
while (true) {
input.getInput();
name = input.getName();
room = input.getRoom();
pwd = input.getPassword();
8.7 Sample Development 481
start
openFile
processInputData
wu23399_ch08.qxd 12/15/06 19:58 Page 481
8.7 Sample Development—continued
validate(name, room, pwd);
}
}
private void validate(String name, String room, String password) {
Resident res = manager.getResident(name);
if (res == null) {
System.out.println(Invalid Entry);
} else if (res.getName().equals(name) 
res.getRoom().equals(room) 
res.getPassword().equals(password)) {
door.open();
} else {
System.out.println(Invalid Entry);
}
}
}
482 Chapter 8 Exceptions and Assertions
Notice that the loop inside the processInputData method is an infinite loop. In
other words, when the program starts, it will execute indefinitely. To terminate such a
program, you must either close the Command window or select an appropriate menu
choice (or click on a toolbar icon) in your Java IDE. We will discuss another way to ter-
minate the program in step 3.
The purpose of step 2 testing is to verify the correct behavior of an InputHandler
object. We need to test both successful and unsuccessful cases. We must verify that the
door is in fact opened when valid information is entered. We must also verify that the
error message is displayed when there’s an error in input. We should test invalid cases
such as entering nonexistent name, corrent name but wrong password, not entering all
information,and so forth.
Step 3 Development: Improve and Finalize
There are several key improvements we can make to the program. The first and foremost
is the improved user interface. Instead of getting three pieces of data individually by
using a scanner, it would be nicer to have a frame window such as the one shown in
Figure 8.10,where the user can enter all three pieces of information.We will describe how
to develop such a frame window in Chapter 14.
step 2 test
validate
wu23399_ch08.qxd 12/15/06 19:58 Page 482
Another improvement is to allow the administrator to terminate the program by
entering special code.This is left as an exercise.
Summary 483
• Two techniques to improve program reliability are exception handling and
assertion.
• Exception handling is another type of control flow.
• An exception represents an error condition, and when it occurs, we say an
exception is thrown.
• A thrown exception must be handled by either catching it or propagating it to
other methods.
• If the program does include code to handle the thrown exceptions, then the
system will handle them.
• A single method can be both a catcher and a propagator of an exception.
• The standard classes described or used in this chapter are
Throwable RuntimeException
Error IllegalArgumentException
Exception InputMismatchException
IOException
• The assertion feature is new to Java 2 SDK 1.4. You must use this version of
the compiler to use assertions in the program.
• The assertion feature is used to detect internal logic errors.
Figure 8.10 A frame window that allows the user to enter the three pieces of information together.
Notice the input entered for the password is displayed back to the user as a sequence of asterisks.
S u m m a r y
wu23399_ch08.qxd 12/15/06 19:58 Page 483
484 Chapter 8 Exceptions and Assertions
E x e r c i s e s
1. Determine the output of the following code when the input is (a) 1, (b) 0,
and (c) 12XY.
Scanner scanner = new Scanner(System.in);
try {
int num = scanner.nextInt();
if (num != 0) {
throw new Exception(Not zero);
}
System.out.println(I'm happy with the input.);
} catch (InputMismatchException e) {
System.out.println(Invalid Entry);
} catch (Exception e) {
System.out.println(Error: + e.getMessage());
}
2. Determine the output of the following code when the input is (a) 1, (b) 0,
and (c) 12XY. This is the same question as Exercise 1, but the code here has
the finally clause.
Scanner scanner = new Scanner(System.in);
try {
int num = scanner.nextInt();
if (num != 0) {
throw new Exception(Not zero);
}
System.out.println(I'm happy with the input.);
} catch (InputMismatchException e) {
System.out.println(Invalid Entry);
K e y C o n c e p t s
exceptions
try-catch
finally
throws
throw
exception hierarchy
programmer-defined exceptions
assertions
precondition assertions
postcondition assertions
wu23399_ch08.qxd 12/15/06 19:58 Page 484
} catch (Exception e) {
System.out.println(Error: + e.getMessage());
} finally {
System.out.println(Finally Clause Executed);
}
3. Why is the following code not a good use of the assertion?
public void compute(int size) {
assert size  0;
//computation code comes here
}
4. Modify the following code by adding the assert statement. The value of
gender is either MALE or FEMALE if the program is running correctly.
switch (gender) {
case MALE: totalFee = tuition + parkingFee;
break;
case FEMALE: totalFee = tuition + roomAndBoard;
break;
}
5. Modify the following method by adding the assert statement. Assume the
variable factor is a data member of the class.
public double compute(double value) {
return (value * value) / factor;
}
6. Modify the getInput method of the InputHandler class from Section 8.7 so
that the method will throw an exception when a blank string (a sequence of
one or more blank spaces) is entered for the name, room, or password.
Define a new exception class EmptyInputException.
7. The user module of the keyless entry system in Section 8.7 does not include
any logic to terminate the program. Modify the program so it will terminate
when the values Admin, X123, and $maTrix%TwO$ are entered for name,
room, and password, respectively.
Development Exercises
For the following exercises, use the incremental development methodology to
implement the program. For each exercise, identify the program tasks, create
a design document with class descriptions, and draw the program diagram.
Map out the development steps at the start. Present any design alternatives and
justify your selection. Be sure to perform adequate testing at the end of each
development step.
Exercises 485
wu23399_ch08.qxd 12/15/06 19:58 Page 485
8. In the sample development, we developed the user module of the keyless
entry system. For this exercise, implement the administrative module that
allows the system administrator to add and delete Resident objects and
modify information on existing Resident objects. The module will also allow
the user to open a list from a file and save the list to a file. Is it proper to
implement the administrative module by using one class? Wouldn’t it be
a better design if we used multiple classes with each class doing a single,
well-defined task?
9. Write an application that maintains the membership lists of five social clubs
in a dormitory. The five social clubs are the Computer Science Club, Biology
Club, Billiard Club, No Sleep Club, and Wine Tasting Club. Use the Dorm
class to manage the membership lists. Members of the social clubs are
Resident objects of the dorm. Use a separate file to store the membership
list for each club. Allow the user to add, delete, and modify members of
each club.
486 Chapter 8 Exceptions and Assertions
wu23399_ch08.qxd 12/15/06 19:58 Page 486
Characters and Strings
O b j e c t i v e s
After you have read and studied this chapter,you should be able to
• Declare and manipulate data of the char type.
• Write string processing programs,using
String,StringBuilder, and StringBuffer
objects.
• Specify regular expressions for searching a
pattern in a string.
• Differentiate the String,StringBuilder, and
StringBuffer classes and use the correct class
in solving a given task.
• Tell the difference between equality and
equivalence testings for String objects.
• Use the Pattern and Matcher classes.
487
9
wu23399_ch09.qxd 12/15/06 20:05 Page 487
arly computers in the 1940s and 1950s were more like gigantic calculators because
they were used primarily for numerical computation. However, as computers have
evolved to possess more computational power, our use of computers is no longer
limited to numerical computation. Today we use computers for processing infor-
mation of diverse types. In fact, most application software today such as Web
browsers, word processors, database management systems, presentation software,
and graphics design software is not intended specifically for number crunching.
These programs still perform numerical computation, but their primary data are
text, graphics, video, and other nonnumerical data. We have already seen examples
of nonnumerical data processing. We introduced the String class and string process-
ing in Chapter 2. A nonnumerical data type called boolean was used in Chapters 5
and 6. In this chapter, we will delve more deeply into the String class and present
advanced string processing. We will also introduce the char data type for represent-
ing a single character and the StringBuffer class for an efficient operation on a
certain type of string processing.
9.1 Characters
In Java single characters are represented by using the data type char. Character
constants are written as symbols enclosed in single quotes, for example, ‘a’, ‘X’,
and ‘5’. Just as we use different formats to represent integers and real numbers
using 0s and 1s in computer memory, we use special codes of 0s and 1s to represent
single characters. For example, we may assign 1 to represent ’A’ and 2 to repre-
sent ‘B’. We can assign codes similarly to lowercase letters, punctuation marks,
digits, and other special symbols. In the early days of computing, different com-
puters used not only different coding schemes but also different character sets. For
example, one computer could represent the symbol 1
⁄4, while other computers
could not. Individualized coding schemes did not allow computers to share infor-
mation. Documents created by using one scheme are complete gibberish if we
try to read these documents by using another scheme. To avoid this problem, U.S.
computer manufacturers devised several coding schemes. One of the coding
schemes widely used today is ASCII (American Standard Code for Information
Interchange). We pronounce ASCII “ăs kē.” Table 9.1 shows the 128 standard
ASCII codes.
Adding the row and column indexes gives you the ASCII code for a
given character. For example, the value 87 is the ASCII code for the character
‘W’. Not all characters in the table are printable. ASCII codes 0 through 31
and 127 are nonprintable control characters. For example, ASCII code 7 is the
bell (the computer beeps when you send this character to output), and code 9
is the tab.
488 Chapter 9 Characters and Strings
I n t r o d u c t i o n
E
char
ASCII
wu23399_ch09.qxd 12/15/06 20:05 Page 488
To represent all 128 ASCII codes, we need 7 bits ranging from 000 0000 (0)
to 111 1111 (127). Although 7 bits is enough, ASCII codes occupy 1 byte (8 bits)
because the byte is the smallest unit of memory you can access. Computer manu-
facturers use the extra bit for other nonstandard symbols (e.g., lines and boxes).
Using 8 bits, we can represent 256 symbols in total—128 standard ASCII codes and
128 nonstandard symbols.
9.1 Characters 489
When we use a word processor to create a document, the file that contains the
document includes not only the contents but also the formatting information.
Since each software company uses its own coding scheme for storing this informa-
tion, we have to use the same word processor to open the document. Often it is
even worse.We cannot open a document created by a newer version of the same
word processor with an older version. If we just want to exchange the text of a
document, then we can convert it to ASCII format. Any word processor can open
and save ASCII files. If we would like to retain the formatting information also,
we can convert the document, using software such as Adobe Acrobat. This soft-
ware converts a document (including text, formatting, images, etc.) created by
different word processors to a format called PDF. Anybody with a free Acrobat
Reader can open a PDF file. Many of the documents available from our website
are in this PDF format.
Table
Table 9.1 ASCII codes
0 1 2 3 4 5 6 7 8 9
0 nul soh stx etx eot enq ack bel bs ht
10 lf vt ff cr so si dle dc1 dc2 dc3
20 cd4 nak syn etb can em sub esc fs gs
30 rs us sp !  # $ %  '
40 ( ) * + , - . / 0 1
50 2 3 4 5 6 7 8 9 : ;
60  =  ? @ A B C D E
70 F G H I J K L M N O
80 P Q R S T U V W X Y
90 Z [  ] ^ _ ` a b c
100 d e f g h i j k l m
110 n o p q r s t u v w
120 x y z { | } ~ del
wu23399_ch09.qxd 12/15/06 20:05 Page 489
The standard ASCII codes work just fine as long as we are dealing with the
English language because all letters and punctuation marks used in English are
included in the ASCII codes. We cannot say the same for other languages. For lan-
guages such as French and German, the additional 128 codes may be used to repre-
sent character symbols not available in standard ASCII. But what about different
currency symbols? What about non-European languages? Chinese, Japanese, and
Korean all use different coding schemes to represent their character sets. Eight bits
is not enough to represent thousands of ideographs. If we try to read Japanese char-
acters by using ASCII, we will see only meaningless symbols.
To accommodate the character symbols of non-English languages, the
Unicode Consortium established the Unicode Worldwide Character Standard,
commonly known simply as Unicode, to support the interchange, processing, and
display of the written texts of diverse languages. The standard currently contains
34,168 distinct characters, which cover the major languages of the Americas,
Europe, the Middle East, Africa, India, Asia, and Pacifica. To accommodate such a
large number of distinct character symbols, Unicode characters occupy 2 bytes.
Unicode codes for the character set shown in Table 9.1 are the same as ASCII
codes.
Java, being a language for the Internet, uses the Unicode standard for repre-
senting char constants. Although Java uses the Unicode standard internally to store
characters, to use foreign characters for input and output in our programs, the oper-
ating system and the development tool we use for Java programs must be capable of
handling the foreign characters.
Characters are declared and used in a manner similar to data of other types.
The declaration
char ch1, ch2 = 'X';
declares two char variables ch1 and ch2 with ch2 initialized to ‘X’. We can display
the ASCII code of a character by converting it to an integer. For example, we can
execute
System.out.println(ASCII code of character X is 
+ (int)'X' );
Conversely, we can see a character by converting its ASCII code to the char data
type, for example,
System.out.println(
Character with ASCII code 88 is  + (char)88 );
Because the characters have numerical ASCII values, we can compare charac-
ters just as we compare integers and real numbers. For example, the comparison
'A'  'c'
returns true because the ASCII value of ‘A’ is 65 while that of ‘c’ is 99.
490 Chapter 9 Characters and Strings
Unicode
wu23399_ch09.qxd 12/15/06 20:05 Page 490
9.2 Strings 491
1. Determine the output of the following statements.
a. System.out.println( (char) 65 );
b. System.out.println( (int) 'C' );
c. System.out.println( 'Y' );
d. if ( 'A'  '?' )
System.out.println( 'A' );
else
System.out.println( '?' );
2. How many distinct characters can you represent by using 8 bits?
9.2 Strings
A string is a sequence of characters that is treated as a single value. Instances of the
String class are used to represent strings in Java. Rudimentary string processing
was already presented in Chapter 2, using methods such as substring, length, and
indexOf. In this section we will learn more advanced string processing, using other
methods of the String class.
To introduce additional methods of the String class, we will go through a num-
ber of common string processing routines. The first is to process a string looking for
a certain character or characters. Let’s say we want to input a person’s name and de-
termine the number of vowels that the name contains. The basic idea is very simple:
for each character ch in the string {
if (ch is a vowel) {
increment the counter
}
}
There are two details we need to know before being able to translate that into actual
code. First, we need to know how to refer to an individual character in the string.
Second, we need to know how to determine the size of the string, that is, the num-
ber of characters the string contains, so we can write the boolean expression to stop
the loop correctly. We know from Chapter 2 that the second task is done by using
the length method. For the first task, we use charAt.
We access individual characters of a string by calling the charAt method of
the String object. For example, to display the individual characters of the string
Sumatra one at a time, we can write
String name = Sumatra;
int size = name.length( );
for (int i = 0; i  size; i++) {
System.out.println(name.charAt(i));
}
Each character in a string has an index that we use to access the character. We
use zero-based indexing; that is, the first character has index 0, the second character
String
charAt
wu23399_ch09.qxd 12/15/06 20:05 Page 491
has index 1, the third character has index 2, and so forth. To refer to the first char-
acter of name, for example, we say
name.charAt(0)
Since the characters are indexed from 0 to size-1, we could express the pre-
ceding for loop as
for (int i = 0; i = size - 1; i++)
However, we will use the first style almost exclusively to be consistent.
Figure 9.1 illustrates how the charAt method works. Notice that name refers
to a String object, and we are calling its charAt method that returns a value of prim-
itive data type char. Strictly speaking, we must say “name is a variable of type
String whose value is a reference to an instance of String.” However, when the value
of a variable X is a reference to an instance of class Y, we usually say “X is an
instance of Y” or “X is a Y object.”
492 Chapter 9 Characters and Strings
S u m r a
a t
0 1 2 5 6
3 4
String name = Sumatra;
name
The variable refers to the
whole string.
name.charAt(3)
The method returns the
character at position 3.
Figure 9.1 An indexed expression is used to refer to individual characters in a string.
If the value of a variable X is a reference to an object of class Y,then we say“ X is a Y
object”or“ X is an instance of Y.”
Since String is a class, we can create an instance of a class by using the new
method. The statements we have been using so far, such as
String name1 = Kona;
String name2;
name2 = Espresso;
work as a shorthand for
String name1 = new String(Kona);
String name2;
name2 = new String(Espresso);
wu23399_ch09.qxd 12/15/06 20:05 Page 492
Be aware that this shorthand works for the String class only. Moreover, although the
difference will not be critical in almost all situations, they are not exactly the same.
We will discuss the subtle difference between the two in Section 9.5.
Here is the code for counting the number of vowels:
9.2 Strings 493
/*
Chapter 9 Sample Program: Count the number of vowels
in a given string
File: Ch9CountVowels.java
*/
import java.util.*;
class Ch9CountVowels {
public static void main (String[] args) {
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
String name;
int numberOfCharacters,
vowelCount = 0;
char letter;
System.out.print(What is your name?);
name = scanner.next( );
numberOfCharacters = name.length( );
for (int i = 0; i  numberOfCharacters; i++) {
letter = name.charAt(i);
if (letter == 'a' || letter == 'A' ||
letter == 'e' || letter == 'E' ||
letter == 'i' || letter == 'I' ||
letter == 'o' || letter == 'O' ||
letter == 'u' || letter == 'U' ) {
vowelCount++;
}
}
System.out.println(name + , your name has  +
vowelCount +  vowels);
}
}
wu23399_ch09.qxd 12/15/06 20:05 Page 493
494 Chapter 9 Characters and Strings
We can shorten the boolean expression in the if statement by using the
toUpperCase method of the String class. This method converts every character in a
string to uppercase. Here’s the rewritten code:
/*
Chapter 9 Sample Program: Count the number of vowels
in a given string using toUpperCase
File: Ch9CountVowels2.java
*/
import java.util.*;
class Ch9CountVowels2 {
public static void main (String[] args) {
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator?));
String name, nameUpper;
int numberOfCharacters,
vowelCount = 0;
char letter;
System.out.print(What is your name?);
name = scanner.next( );
numberOfCharacters = name.length( );
nameUpper = name.toUpperCase( );
for (int i = 0; i  numberOfCharacters; i++) {
letter = nameUpper.charAt(i);
if (letter == 'A' ||
letter == 'E' ||
letter == 'I' ||
letter == 'O' ||
letter == 'U' ) {
vowelCount++;
}
}
(name + , your name has  +
vowelCount +  vowels);
}
}
wu23399_ch09.qxd 12/15/06 20:05 Page 494
Bad Version
Bad Version
Notice that the original string name is unchanged. A new, converted string is
returned from the toUpperCase method and assigned to the second String variable
nameUpper.
Let’s try another example. This time we read in a string and count how many
words the string contains. For this example we consider a word as a sequence of
characters separated, or delimited, by blank spaces. We treat punctuation marks and
other symbols as part of a word. Expressing the task in pseudocode, we have the
following:
read in a sentence;
while (there are more characters in the sentence) {
look for the beginning of the next word;
now look for the end of this word;
increment the word counter;
}
We use a while loop here instead of do–while to handle the case when the
input sentence contains no characters, that is, when it is an empty string. Let’s
implement the routine. Here’s our first attempt:
//Attempt No. 1
static final char BLANK = ' ';
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator ));
int index, wordCount, numberOfCharacters;
System.out.println(Enter a sentence: );
String sentence = scanner.next( );
numberOfCharacters = sentence.length();
index = 0;
wordCount = 0;
while (index  numberOfCharacters ) {
//ignore blank spaces
while (sentence.charAt(index) == BLANK) {
index++;
}
//now locate the end of the word
while (sentence.charAt(index) != BLANK) {
index++;
}
//another word has been found, so increment the counter
wordCount++;
}
9.2 Strings 495
toUpperCase
Skip blank spaces until
a character that is not a
blank space is encoun-
tered.This is the begin-
ning of a word.
Once the beginning of
a word is detected,we
skip nonblank charac-
ters until a blank space
is encountered.This is
the end of the word.
wu23399_ch09.qxd 12/15/06 20:05 Page 495
This implementation has a problem. The counter variable index is incre-
mented inside the two inner while loops, and this index could become equal to
numberOfCharacters, which is an error, because the position of the last character
is numberOfCharacters – 1. We need to modify the two while loops so that index
will not become larger than numberOfCharacters –1. Here’s the modified code:
496 Chapter 9 Characters and Strings
/*
Chapter 9 Sample Program: Count the number of words
in a given string
File: Ch9CountWords.java (Attempt 2)
*/
import java.util.*;
class Ch9CountWords { //Attempt 2
private static final char BLANK = ' ';
public static void main (String[] args) {
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator ));
int index, wordCount, numberOfCharacters;
System.out.println(Enter a sentence: );
String sentence = scanner.next( );
numberOfCharacters = sentence.length( );
index = 0;
wordCount = 0;
while ( index  numberOfCharacters ) {
//ignore blank spaces
while (index  numberOfCharacters 
sentence.charAt(index) == BLANK) {
index++;
}
//now locate the end of the word
while (index  numberOfCharacters 
sentence.charAt(index) != BLANK) {
index++;
}
//another word is found, so increment the counter
wordCount++;
}
wu23399_ch09.qxd 12/15/06 20:05 Page 496
//display the result
System.out.println( n input sentence:  + sentence );
System.out.println(  Word count:  + wordCount +  words );
}
}
9.2 Strings 497
Notice that the order of comparisons in the boolean expression
index  numberOfCharacters
 sentence.charAt(index) == BLANK
is critical. If we switch the order to
sentence.charAt(index) == BLANK
 index  numberOfCharacters
and if the last character in the string is a space, then an out-of-bound exception will
occur because the value of index is a position that does not exist in the string
sentence. By putting the expression correctly as
index  numberOfCharacters  sentence.charAt(index) != ' '
we will not get an out-of-bound exception because the boolean operator  is
a shortcircuit operator. If the relation index  numberOfCharacters is false, then
the second half of the expression sentence.charAT(index) != BLANK will not get
evaluated.
There is still a problem with the attempt 2 code. If the sentence ends with one
or more blank spaces, then the value for wordCount will be 1 more than the actual
number of words in the sentence. It is left as an exercise to correct this bug (see
Exercise 15 at the end of the chapter).
Our third example counts the number of times the word Java occurs in
the input. The repetition stops when the word STOP is read. Lowercase and upper-
case letters are not distinguished when an input word is compared to Java, but
the word STOP for terminating the loop must be in all uppercase letters. Here’s the
pseudocode:
javaCount = 0;
while (true) {
read in next word;
if (word is STOP) {
break;
out-of-bound
exception
wu23399_ch09.qxd 12/15/06 20:05 Page 497
} else if (word is Java ignoring cases) {
javaCount++;
}
}
And here’s the actual code. Pay close attention to how the strings are
compared.
498 Chapter 9 Characters and Strings
/*
Chapter 9 Sample Program:
Count the number of times the word 'java' occurs
in input. Case-insensitive comparison is used here.
The program terminates when the word STOP (case-sensitive)
is entered.
File: Ch9CountJava.java
*/
import java.util.*;
class Ch9CountJava {
public static void main (String[] args) {
Scanner scanner = new Scanner(System.in);
int javaCount = 0;
String word;
while (true) {
System.out.print(Next word: );
word = scanner.next( );
if (word.equals(STOP) ) {
break;
} else if (word.equalsIgnoreCase(Java) ) {
javaCount++;
}
}
System.out.println('Java' count:  + javaCount );
}
}
wu23399_ch09.qxd 12/15/06 20:05 Page 498
String comparison is done by two methods—equals and equalsIgnoreCase—
whose meanings should be clear from the example. Another comparison method is
compareTo. This method compares two String objects str1 and str2 as in
str1.compareTo( str2 );
and returns 0 if they are equal, a negative integer if str1 is less than str2, and a pos-
itive integer if str1 is greater than str2. The comparison is based on the lexicographic
order of Unicode. For example, caffeine is less than latte. Also, the string jaVa is
less than the string java because the Unicode value of V is smaller than the Unicode
value of v. (See the ASCII table, Table 9.1.)
Some of you may be wondering why we don’t say
if ( word == STOP )
We can, in fact, use the equality comparison symbol == to compare two String
objects, but the result is different from the result of the method equals. We will
explain the difference in Section 9.5.
Let’s try another example, using the substring method we introduced in
Chapter 2. To refresh our memory, here’s how the method works. If str is a String
object, then the expression
str.substring ( beginIndex, endIndex )
returns a new string that is a substring of str from position beginIndex to endIndex – 1.
The value of beginIndex must be between 0 and str.length() – 1, and the value of
endIndex must be between 0 and str.length(). In addition, the value of beginIndex
must be less than or equal to the value of endIndex. Passing invalid values for
beginIndex or endIndex will result in a runtime error.
In this example, we print out the words from a given sentence, using one line
per word. For example, given an input sentence
I want to be a Java programmer
the code will print out
I
want
to
be
a
Java
programmer
9.2 Strings 499
compareTo
wu23399_ch09.qxd 12/15/06 20:05 Page 499
This sample code is similar to the previous one that counts the number
of words in a given sentence. Instead of just counting the words, we need to extract
the word from the sentence and print it out. Here’s how we write the code:
500 Chapter 9 Characters and Strings
/*
Chapter 9 Sample Program:
Extract the words in a given sentence and
print them, using one line per word.
File: Ch9ExtractWords.java
*/
import java.util.*;
class Ch9ExtractWords {
private static final char BLANK = ' ';
public static void main (String[] args) {
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
int index, numberOfCharacters,
beginIdx, endIdx;
String word, sentence;
System.out.print(Input: );
Sentence = scanner.next( );
numberOfCharacters = sentence.length();
index = 0;
while (index  numberOfCharacters) {
//ignore leading blank spaces
while (index  numberOfCharacters 
sentence.charAt(index) == BLANK) {
index++;
}
beginIdx = index;
//now locate the end of the word
while (index  numberOfCharacters 
sentence.charAt(index) != BLANK) {
index++;
}
wu23399_ch09.qxd 12/15/06 20:05 Page 500
endIdx = index;
if (beginIdx != endIdx) {
//another word is found, extract it from the
//sentence and print it out
word = sentence.substring( beginIdx, endIdx );
System.out.println(word);
}
}
}
}
9.2 Strings 501
Notice the signficance of the test
if (beginIdx != endIdx)
in the code. For what kinds of input sentences will the variables beginIdx and
endIdx be equal? We’ll leave this as an exercise (see Exercise 16 at the end of the
chapter).
1. Determine the output of the following code.
a. String str = Programming;
for (int i = 0; i  9; i+=2) {
System.out.print( str.charAt( i ) );
}
b. String str = World Wide Web;
for (int i = 0; i  10; i ++ ) {
if ( str.charAt(i) == 'W') {
System.out.println( 'M' );
} else {
System.out.print( str.charAt(i) );
}
}
2. Write a loop that prints out a string in reverse. If the string is Hello, then the
code outputs olleH.
wu23399_ch09.qxd 12/15/06 20:05 Page 501
502 Chapter 9 Characters and Strings
3. Assume two String objects str1 and str2 are initialized as follows:
String str1 = programming;
String str2 = language;
Determine the value of each of the following expressions if they are valid. If
they are not valid, state the reason why.
a. str1.compareTo( str2 )
b. str2.compareTo( str2 )
c. str2.substring( 1, 1 )
d. str2.substring( 0, 7 )
e. str2.charAt( 11 )
f. str1.length( ) + str2.length( )
4. What is the difference between the two String methods equals and
equalsIgnoreCase?
9.3 Pattern Matching and Regular Expression
One sample code from Section 9.2 searched for the word Java in a given string. This
sample code illustrated a very simplified version of a well-known problem called
pattern matching. Word processor features such as finding a text and replacing a
text with another text are two specialized cases of a pattern-matching problem.
The matches Method
Let’s begin with the matches method from the String class. In its simplest form, it
looks very similar to the equals method. For example, given a string str, the two
statements
str.equals(Hello);
str.matches(Hello);
both evaluate to true if str is the string Hello. However, they are not truly equivalent,
because, unlike equals, the argument to the matches method can be a pattern, a fea-
ture that brings great flexibility and power to the matches method.
Suppose we assign a three-digit code to all incoming students. The first digit
represents the major, and 5 stands for the computer science major. The second digit
represents the home state: 1 is for in-state students, 2 is for out-of-state students, and
3 is for foreign students. And the third digit represents the residence of the student.
On-campus dormitories are represented by digits from 1 through 7. Students living
off campus are represented by digit 8. For example, the valid encodings for students
majoring in computer science and living off campus are 518, 528, and 538. The
valid three-digit code for computer science majors living in one of the on-campus
dormitories can be expressed succinctly as
5[123][1-7]
pattern
matching
wu23399_ch09.qxd 12/15/06 20:05 Page 502
and here’s how we interpret the pattern:
The pattern is called a regular expression that allows us to denote a large (often in-
finite) set of words succinctly. The “word” is composed of any sequence of symbols
and is not limited to alphabets. The brackets [ ] are used here to represent choices,
so [123] means 1, 2, or 3. We can use the notation for alphabets also. For example,
[aBc] means a, B, or c. Notice the notation is case-sensitive. The hyphen in the brack-
ets shows the range, so [1-7] means any digit from 1 to 7. If we want to allow any
lowercase letter, then the regular expression will be [a-z]. The hat symbol ^ is used
for negation. For example, [^abc] means any character except a, b, or c. Notice that
this expression does not restrict the character to lowercase letters; it can be any
character including digits and symbols. To refer to all lowercase letters except a, b,
or c, the correct expression is [a-z[^abc]]. The double ampersand represents an
intersection. Here are more examples:
It must be
1, 2, or 3.
It must be any
digit from 1 to 7.
It must be 5 for
the computer
science majors.
first
digit
second
digit
third
digit
5 [123] [1–7]
9.3 Pattern Matching and Regular Expression 503
regular
expression
Expression Description
[013] A single digit 0,1,or 3.
[0–9][0–9] Any two-digit number from 00 to 99.
A[0–4]b[05] A string that consists of four characters.The first
character is A.The second character is 0,1,2,3,or 4.
The third character is b.And the last character is
either 0 or 5.
[0–9[ˆ4567]] A single digit that is 0,1,2,3,8,or 9.
[a–z0–9] A single character that is either a lowercase letter or a
digit.
We can use repetition symbols * or + to designate a sequence of unbounded
length. The symbol * means 0 or more times, and the symbol + means 1 or more
times. Let’s try an example using a repetition symbol. Remember the definition
for a valid Java identifier? We define it as a seqence of alphanumeric characters,
underscores, and dollar signs, with the first character being an alphabet. In regular
expression, we can state this definition as
[a-zA-Z][a-zA-Z0-9_$]*
wu23399_ch09.qxd 12/15/06 20:05 Page 503
Let’s write a short program that will input a word and determine whether it is
a valid Java identifier. The program stops when the word entered is STOP. Here’s the
program:
504 Chapter 9 Characters and Strings
/*
Chapter 9 Sample Program: Checks whether the input
string is a valid identifier.
File: Ch9MatchJavaIdentifier.java
*/
import java.util.*;
class Ch9MatchJavaIdentifier {
private static final String STOP = STOP;
private static final String VALID = Valid Java identifier;
private static final String INVALID = Not a valid Java identifier;
private static final String VALID_IDENTIFIER_PATTERN
= [a-zA-Z][a-zA-Z0-9_$]*;
public static void main (String[] args) {
Scanner scanner = new Scanner (System.in);
String str, reply;
while (true) {
System.out.print (Identifier: );
str = scanner.next( );
if (str.equals(STOP)) break;
if (str.matches(VALID_IDENTIFIER_PATTERN)) {
reply = VALID;
} else {
reply = INVALID;
}
System.out.println(str + :  + reply + n);
}
}
}
It is also possible to designate a sequence of fixed length. For example, to spec-
ify four-digit numbers, we write [0-9]{4}. The number in the braces { and } denotes
the number of repetitions. We can specify the minimum and maximum numbers of
wu23399_ch09.qxd 1/11/07 11:50 Page 504
repetitions also. Here are the rules:
Here’s an example of using a sequence of fixed length. Suppose we want to
determine whether the input string represents a valid phone number that follows the
pattern of
xxx-xxx-xxxx
where x is a single digit from 0 through 9. The following is a program that inputs a
string continually and replies whether the input string conforms to the pattern. The
program terminates when a single digit 0 is entered. Structurally this program is
identical to the Ch9MatchJavaIdentifier class. Here’s the program:
Expression Description
X{N} Repeat X exactly N times,where X is a regular
expression for a single character.
X{N,} Repeat X at least N times.
X{N,M} Repeat X at least N but no more than M times.
9.3 Pattern Matching and Regular Expression 505
/*
Chapter 9 Sample Program: Checks whether the input
string conforms to the phone number
pattern xxx-xxx-xxxx.
File: Ch9MatchPhoneNumber.java
*/
import java.util.*;
class Ch9MatchPhoneNumber {
private static final String STOP = 0;
private static final String VALID = Valid phone number;
private static final String INVALID = Not a valid phone number;
private static final String VALID_PHONE_PATTERN
= [0-9]{3}-[0-9]{3}-[0-9]{4};
public static void main (String[] args) {
Scanner scanner = new Scanner (System.in);
String phoneStr, reply;
while (true) {
System.out.print (Phone#: );
phoneStr = scanner.next( );
wu23399_ch09.qxd 12/15/06 20:05 Page 505
Suppose, with the proliferation of cell phones, the number of digits used for a
prefix increases from three to four in major cities. (In fact, Tokyo now uses a four-
digit prefix. Phenomenal growth in the use of fax machines in both offices and
homes caused the increase from three to four digits.) The valid format for phone
numbers then becomes
xxx-xxx-xxxx or xxx-xxxx-xxxx
This change can be handled effortlessly by defining VALID_PHONE_PATTERN as
private static final String VALID_PHONE_PATTERN
= [0-9]{3}-[0-9]{3,4}-[0-9]{4};
This is the power of regular expression and pattern-matching methods. All we
need to do is to make one simple adjustment to the regular expression. No other
changes are made to the program. Had we written the program without using the
pattern-matching technique (i.e., written the program using repetition control to test
the first to the last character individually), changing the code to handle both a three-
digit and a four-digit prefix requires substantially greater effort.
The period symbol (.) is used to match any character except a line terminator
such as n or r. (By using the Pattern class, we can make it match a line terminator
also. We discuss more details on the Pattern class later.) We can use the period sym-
bol with the zero-or-more-times notation * to check if a given string contains a
sequence of characters we are looking for. For example, suppose a String object
document holds the content of some document, and we want to check if the phrase
“zen of objects” is in it. We can do it as follows:
String document;
document = ...; //assign text to 'document'
if (document.matches(.*zen of objects.*) {
System.out.println(Found);
506 Chapter 9 Characters and Strings
if (phoneStr.equals(STOP)) break;
if (phoneStr.matches(VALID_PHONE_PATTERN)) {
reply = VALID;
} else {
reply = INVALID;
}
System.out.println(phoneStr + :  + reply + n);
}
}
}
wu23399_ch09.qxd 12/15/06 20:05 Page 506
} else {
System.out.println(Not found);
}
The brackets [ and ] are used for expressing a range of choices for a single
character. If we need to express a range of choices for multiple characters, then we
use the parentheses and the vertical bar. For example, if we search for the word
maximum or minimum, we express the pattern as
(max|min)imum
Here are some more examples:
The replaceAll Method
Using the replaceAll method, we can replace all occurrences of a substring that
matches a given regular expression with a given replacement string. For example,
here’s how to replace all vowels in the string with the @ symbol:
String originalText, modifiedText;
originalText = ...; //assign string to 'originalText'
modifiedText = originalText.replaceAll([aeiou], @);
Notice the original text is unchanged. The replaceAll method returns a modified text
as a separate string. Here are more examples:
Expression Description
[wb](ad|eed) Matches wad,weed,bad,and beed.
(pro|anti)-OO? Matches pro-OOP and anti-OOP.
(AZ|CA|CO)[0–9]{4} Matches AZxxxx,CAxxxx,and COxxxx,
where x is a single digit.
9.3 Pattern Matching and Regular Expression 507
Expression Description
str.replaceAll(OOP, Replace all occurrences of OOP with
object-oriented programming) object-oriented programming.
str.replaceAll( Replace all social security numbers
[0-9]{3}-[0-9]{2}-[0-9]{4}, with xxx-xx-xxxx.
xxx-xx-xxxx)
str.replaceAll(o{2,}, oo) Replace all occurrences of a sequence
that has two or more of letter o with oo.
wu23399_ch09.qxd 12/15/06 20:05 Page 507
If we want to match only the whole word, we have to use the b symbol to des-
ignate the word boundary. Suppose we write
str.replaceAll(temp, temporary);
expecting to replace all occurrences of the abbreviated word temp by temporary.
We will get a surprising result. All occurrences of the sequence of characters temp
will be replaced; so, for example, words such as attempt or tempting would be
replaced by attemporaryt or temporaryting, respectively. To designate the sequence
temp as a whole word, we place the word boundary symbol b in the front and end
of the sequence.
str.replaceAll(btempb, temporary);
Notice the use of two backslashes. The symbol we use in the regular expres-
sion is b. However, we must write this regular expression in a String representation.
And remember that the backslash symbol in a string represents a control character
such as n, t, and r. To specify the regular expression symbol with a backslash, we
must use additional backslash, so the system will not interpret it as some kind of
control character. The regular expression we want here is
btempb
To put it in a String representation, we write
btempb
Here are the common backslash symbols used in regular expressions:
508 Chapter 9 Characters and Strings
String
Expression Representation Description
d d A single digit.Equivalent to [0–9].
D D A single nondigit.Equivalent to [^0–9].
s s A white space character,such as space,
tab,new line,etc.
S S A non-white-space character.
w w A word character.Equivalent to
[a–zA–Z_0–9].
W W A nonword character.
b b A word boundary (such as a white space
and punctuation mark).
B B A nonword boundary.
We also use the backslash if we want to search for a command character. For
example, the plus symbol designates one or more repetitions. If we want to search
for the plus symbol in the text, we use the backslash as + and to express it as a
wu23399_ch09.qxd 12/15/06 20:05 Page 508
string, we write “+”. Here’s an example. To replace all occurrences of C and C++
(not necessarily a whole word) with Java, we write
str.replaceAll((C|C++), Java);
9.4 The Pattern and Matcher Classes 509
1. Describe the string that the following regular expressions match.
a. a*b
b. b[aiu]d
c. [Oo]bject(s| )
2. Write a regular expression for a state vehicle license number whose format is
a single capital letter, followed by three digits and four lowercase letters.
3. Which of the following regular expressions are invalid?
a. (a-z)*+
b. [a|ab]xyz
c. abe-14
d. [a-z^a^b]
e. [//one]two
9.4 The Pattern and Matcher Classes
The matches and replaceAll methods of the String class are shorthand for using the
Pattern and Matcher classes from the java.util.regex package. We will describe how
to use these two classes for more efficient pattern matching.
The statement
str.matches(regex);
where str and regex are String objects is equivalent to
Pattern.matches(regex, str);
which in turn is equivalent to
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
matcher.matches();
Similarly, the statement
str.replaceAll(regex, replacement);
where replacement is a replacement text is equivalent to
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
matcher.replaceAll(replacement);
wu23399_ch09.qxd 12/15/06 20:05 Page 509
Explicit creation of Pattern and Matcher objects gives us more options and
greater efficiency. We specify regular expressions as strings, but for the system to
actually carry out the pattern-matching operation, the stated regular expression
must first be converted to an internal format. This is done by the compile method
of the Pattern class. When we use the matches method of the String or Pattern
class, this conversion into the internal format is carried out every time the
matches method is executed. So if we use the same pattern multiple times, then it
is more efficient to convert just once, instead of repeating the same conversion,
as was the case for the Ch9MatchJavaIdentifier and Ch9MatchPhoneNumber
classes. The following is Ch9MatchJavaIdentifierPM, a more efficient version of
Ch9MatchJavaIdentifier:
510 Chapter 9 Characters and Strings
/*
Chapter 9 Sample Program: Checks whether the input
string is a valid identifier. This version
uses the Matcher and Pattern classes.
File: Ch9MatchJavaIdentifierPM.java
*/
import java.util.*;
import java.util.regex.*;
class Ch9MatchJavaIdentifierPM {
private static final String STOP = STOP;
private static final String VALID = Valid Java identifier;
private static final String INVALID = Not a valid Java identifier;
private static final String VALID_IDENTIFIER_PATTERN
= [a-zA-Z][a-zA-Z0-9_$]*;
public static void main (String[] args) {
Scanner scanner = new Scanner(System.in);
String str, reply;
Matcher matcher;
Pattern pattern
= Pattern.compile(VALID_IDENTIFIER_PATTERN);
while (true) {
System.out.print(Identifier: );
str = Scanner.next();
if (str.equals(STOP)) break;
matcher = pattern.matcher(str);
if (matcher.matches()) {
reply = VALID;
wu23399_ch09.qxd 12/15/06 20:05 Page 510
} else {
reply = INVALID;
}
System.out.println(str + :  + reply + n);
}
}
}
9.4 The Pattern and Matcher Classes 511
We have a number of options when the Pattern compiles into an internal format.
For example, by default, the period symbol does not match the line terminator char-
acter. We can override this default by passing DOTALL as the second argument as
Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
To enable case-insensitive matching, we pass the CASE_INSENSITIVE constant.
The find method is another powerful method of the Matcher class. This
method searches for the next sequence in a string that matches the pattern. The
method returns true if the patten is found. We can call the method repeatedly until
it returns false to find all matches. Here’s an example that counts the number of
times the word java occurs in a given document. We will search for the word in a
case-insensitive manner.
/*
Chapter 9 Sample Program:
Count the number of times the word 'java' occurs
in input sentence using pattern matching.
File: Ch9CountJavaPM.java
*/
import java.util.*;
import java.util.regex.*;
class Ch9CountJavaPM {
public static void main (String[] args) {
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
String document;
int javaCount;
Matcher matcher;
Pattern pattern = Pattern.compile(java,
Pattern.CASE_INSENSITIVE);
wu23399_ch09.qxd 12/15/06 20:05 Page 511
System.out.println(Sentence: );
document = scanner.next();
javaCount = 0;
matcher = pattern.matcher(document);
while (matcher.find()) {
javaCount++;
}
System.out.println(The word 'java' occurred  +
javaCount +  times.);
}
}
512 Chapter 9 Characters and Strings
When a matcher finds a matching sequence of characters, we can query the
location of the sequence by using the start and end methods. The start method
returns the position in the string where the first character of the pattern is found, and
the end method returns the value 1 more than the position in the string where the
last character of the pattern is found. Here’s the code that prints out the matching
sequences and their locations in the string when searching for the word java in a
case-insensitive manner.
/*
Chapter 9 Sample Program:
Displays the positions of the word 'java'
in a given string using pattern-matching technique.
File: Ch9LocateJavaPM.java
*/
import javax.swing.*;
import java.util.regex.*;
class Ch9LocateJavaPM {
public static void main (String[] args) {
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
String document;
Matcher matcher;
Pattern pattern = Pattern.compile(java,
Pattern.CASE_INSENSITIVE);
wu23399_ch09.qxd 12/15/06 20:05 Page 512
System.out.println(Sentence: );
document = scanner.next();
matcher = pattern.matcher(document);
while (matcher.find()) {
System.out.println(document.substring(matcher.start(),
matcher.end())
+  found at position 
+ matcher.start());
}
}
}
9.5 Comparing Strings 513
1. Replace the following statements with the equivalent ones using the Pattern
and Matcher classes.
a. str.replaceAll(1, one);
b. str.matches(alpha);
2. Using the find method of the Matcher class, check if the given string
document contains the whole word Java.
9.5 Comparing Strings
We already discussed how objects are compared in Chapter 5. The same rule applies
for the string, but we have to be careful in certain situations because of the differ-
ence in the way a new String object is created. First, we will review how the objects
are compared. The difference between
String word1, word2;
...
if ( word1 == word2 ) ...
and
if ( word1.equals(word2) ) ...
is illustrated in Figure 9.2. The equality test == is true if the contents of variables are
the same. For a primitive data type, the contents are values themselves; but for a ref-
erence data type, the contents are addresses. So for a reference data type, the equality
test is true if both variables refer to the same object, because they both contain the
same address. The equals method, on the other hand, is true if the String objects to
which the two variables refer contain the same string value. To distinguish the two
types of comparisons, we will use the term equivalence test for the equals method.
 versus
equals
equivalence
test
wu23399_ch09.qxd 12/15/06 20:05 Page 513
As long as we create a new String object as
String str = new String(Java);
using the new operator, the rule for comparing objects applies to comparing strings.
However, when the new operator is not used, for example, in
String str = Java;
we have to be careful. Figure 9.3 shows the difference in assigning a String object
to a variable. If we do not use the new operator, then string data are treated as if they
514 Chapter 9 Characters and Strings
word1 == word2 is true
word1.equals( word2 ) is true
:String
word1
Case A: Referring to the same object.
Java
word2
Note: If x ⴝⴝ y is true, then x.equals(y) is
also true. The reverse is not always true.
word1 == word2 is false
word1.equals( word2 ) is true
:String
word1
Case B: Referring to different objects having identical string values.
Java
:String
Java
word2
word1 == word2 is false
word1.equals( word2 ) is false
:String
word1
Case C: Referring to different objects having different string values.
Bali
:String
Java
word2
Figure 9.2 The difference between the equality test and the equals method.
wu23399_ch09.qxd 12/15/06 20:05 Page 514
are primitive data type. When we use the same literal String constants in a program,
there will be exactly one String object.
9.6 StringBuffer and StringBuilder 515
String word1, word2;
word1 = new String(Java);
word2 = new String(Java);
Whenever the new operator is used,
there will be a new object.
String word1, word2;
word1 = Java;
word2 = Java;
Literal string constant such as “Java” will
always refer to the one object.
:String
word1
Java
word2
:String
word1
Java
:String
Java
word2
Figure 9.3 Difference between using and not using the new operator for String.
1. Show the state of memory after the following statements are executed.
String str1, str2, str3;
str1 = Jasmine;
str2 = Oolong;
str3 = str2;
str2 = str1;
9.6 StringBuffer and StringBuilder
A String object is immutable, which means that once a String object is created, we
cannot change it. In other words, we can read individual characters in a string, but
we cannot add, delete, or modify characters of a String object. Remember that the
methods of the String class, such as replaceAll and substring, do not modify the
original string; they return a new string. Java adopts this immutability restriction
to implement an efficient memory allocation scheme for managing String objects.
The immutability is the reason why we can treat the string data much as a primitive
data type.
wu23399_ch09.qxd 12/15/06 20:05 Page 515
Creating a new string from the old one will work for most cases, but some
times manipulating the content of a string directly is more convenient. When we
need to compose a long string from a number of words, for example, being able to
manipulate the content of a string directly is much more convenient than creating a
new copy of a string. String manipulation here means operations such as replacing
a character, appending a string with another string, deleting a portion of a string, and
so forth. If we need to manipulate the content of a string directly, we must use either
the StringBuffer or the StringBuilder class. Here’s a simple example of modifying
the string Java to Diva using a StringBuffer object:
StringBuffer word = new StringBuffer( Java );
word.setCharAt(0, 'D');
word.setCharAt(1, 'i');
Notice that no new string is created, the original string Java is modified. Also, we
must use the new method to create a StringBuffer object.
The StringBuffer and StringBuilder classes behave exactly the same (i.e., they
support the same set of public methods), but the StringBuilder class in general has a
better performance. The StringBuilder class is new to Java 2 SDK version 1.5, so it
cannot be used with the older versions of Java SDK. There are advanced cases
where you have to use the StringBuffer class, but for the sample string processing
programs in this book, we can use either one of them. Of course, to use the String-
Builder class, we must be using version 1.5 SDK. We can also continue to use the
StringBuffer class with version 1.5.
Because the StringBuffer class can be used with all versions of Java SDK, and
the string processing performance in not our major concern here, we will be using
the StringBuffer class exclusively in this book. If the string processing performance
is a concern, then all we have to do is to replace all occurrences of the word String-
Buffer to StringBuilder in the program and run it with version 1.5 SDK.
Let’s look at some examples using StringBuffer objects. The first example
reads a sentence and replaces all vowels in the sentence with the character X.
516 Chapter 9 Characters and Strings
string
manipulation
StringBuffer
/*
Chapter 9 Sample Program: Replace every vowel in a given sentence
with 'X' using StringBuffer.
File: Ch9ReplaceVowelsWithX.java
*/
import java.util.*;
class Ch9ReplaceVowelsWithX {
public static void main (String[] args) {
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter(System.getProperty(line.separator));
wu23399_ch09.qxd 12/15/06 20:05 Page 516
Bad Version
StringBuffer tempStringBuffer;
String inSentence;
int numberOfCharacters;
char letter;
System.out.println(Sentence: );
inSentence = scanner.next();
tempStringBuffer = new StringBuffer(inSentence);
numberOfCharacters = tempStringBuffer.length();
for (int index = 0; index  numberOfCharacters; index++) {
letter = tempStringBuffer.charAt(index);
if (letter == 'a' || letter == 'A' ||
letter == 'e' || letter == 'E' ||
letter == 'i' || letter == 'I' ||
letter == 'o' || letter == 'O' ||
letter == 'u' || letter == 'U' ) {
tempStringBuffer.setCharAt(index,'X');
}
}
System.out.println(Input:  + inSentence);
System.out.println(Output:  + tempStringBuffer);
}
}
9.6 StringBuffer and StringBuilder 517
Notice how the input routine is done. We are reading in a String object and
converting it to a StringBuffer object, because we cannot simply assign a String
object to a StringBuffer variable. For example, the following code is invalid:
StringBuffer strBuffer = scanner.next();
We are required to create a StringBuffer object from a String object as in
String str = Hello;
StringBuffer strBuf = new StringBuffer( str );
We cannot input StringBuffer objects. We have to input String objects and
convert them to StringBuffer objects.
wu23399_ch09.qxd 12/15/06 20:05 Page 517
Our next example constructs a new sentence from input words that have an
even number of letters. The program stops when the word STOP is read. Let’s begin
with the pseudocode:
set tempStringBuffer to empty string;
repeat = true;
while ( repeat ) {
read in next word;
if (word is STOP) {
repeat = false;
} else if (word has even number of letters) {
append word to tempStringBuffer;
}
}
And here’s the actual code:
518 Chapter 9 Characters and Strings
/*
Chapter 9 Sample Program: Constructs a new sentence from
input words that have an even number of letters.
File: Ch9EvenLetterWords.java
*/
import javax.swing.*;
class Ch9EvenLetterWords {
public static void main (String[] args) {
Scanner scanner = new Scanner(System.in);
boolean repeat = true;
String word;
StringBuffer tempStringBuffer = new StringBuffer();
while (repeat) {
System.out.print(Next word: );
word = scanner.next( );
if (word.equals(STOP)) {
repeat = false;
} else if (word.length() % 2 == 0) {
tempStringBuffer.append(word +  );
}
}
Create StringBuffer object
with an empty string.
Append word
and a space to
tempStringBuffer.
wu23399_ch09.qxd 12/15/06 20:05 Page 518
System.out.println(Output:  + tempStringBuffer);
}
}
9.6 StringBuffer and StringBuilder 519
We use the append method to append a String or a StringBuffer object to the
end of a StringBuffer object. The method append also can take an argument of the
primitive data type. For example, all the following statements are valid:
int i = 12;
float x = 12.4f;
char ch = 'W';
StringBuffer str = new StringBuffer();
str.append(i);
str.append(x);
str.append(ch);
Any primitive data type argument is converted to a string before it is appended to a
StringBuffer object.
Notice that we can write the second example using only String objects. Here’s
how:
boolean repeat = true;
String word, newSentence;
newSentence = ; //empty string
while (repeat) {
System.out.print(Next word: );
word = scanner.next();
if (word.equals(STOP)) {
repeat = false;
} else if (word.length() % 2 == 0) {
newSentence = newSentence + word;
//string concatenation
}
}
Although this code does not explicitly use any StringBuffer object, the Java
compiler may use StringBuffer when compiling the string concatenation operator.
For example, the expression
newSentence + word
can be compiled as if the expression were
new StringBuffer().append(word).toString()
wu23399_ch09.qxd 12/15/06 20:05 Page 519
Using the append method of StringBuffer is preferable to using the string concate-
nation operator + because we can avoid creating temporary string objects by using
StringBuffer.
In addition to appending a string at the end of StringBuffer, we can insert a string
at a specified position by using the insert method. The syntax for this method is
StringBuffer . insert ( insertIndex, value ) ;
where insertIndex must be greater than or equal to 0 and less than or equal to the
length of StringBuffer and the value is an object or a value of the primitive data
type. For example, to change the string
Java is great
to
Java is really great
we can execute
StringBuffer str = new StringBuffer(Java is great);
str.insert(8, really );
520 Chapter 9 Characters and Strings
1. Determine the value of str after the following statements are executed.
a. StringBuffer str
= new StringBuffer( Caffeine );
str.insert(0, Dr. );
b. String str = Caffeine;
StringBuffer str1 =
new StringBuffer( str.substring(1, 3) );
str1.append('e');
str = De + str1;
c. String str = Caffeine;
StringBuffer str =
new StringBuffer( str.substring(4, 8);
str1.insert ( 3,'f');
str = De + str1
2. Assume a String object str is assigned as a string value. Write a code segment
to replace all occurrences of lowercase vowels in a given string to the letter C
by using String and StringBuffer objects.
3. Find the errors in the following code.
String str = Caffeine;
StringBuffer str1 = str.substring(1, 3);
str1.append('e');
System.out(str1);
str1 = str1 + str;
wu23399_ch09.qxd 12/15/06 20:05 Page 520
Sample Development
Building Word Concordance
One technique to analyze a historical document or literature is to track word occurrences.
A basic form of word concordance is a list of all words in a document and the number of
times each word appears in the document. Word concordance is useful in revealing the
writing style of an author.For example,given a word concordance of a document,we can
scan the list and count the numbers of nouns, verbs, prepositions, and so forth. If the
ratios of these grammatical elements differ significantly between the two documents,
there is a high probability that they are not written by the same person. Another appli-
cation of word concordance is seen in the indexing of a document, which, for each
word, lists the page numbers or line numbers where it appears in the document. In this
sample development, we will build a word concordance of a given document, utilizing
the string-processing technique we learned in this chapter.
9.7 Sample Development 521
9.7 Sample Development
word con-
cordance
One of the most popular search engine websites on the Internet today is
Google (www.google.com). At the core of their innovative technology is a
concordance of all Web pages on the Internet. Every month the company’s
Web crawler software visits 3 billion (and steadily growing) Web pages, and
from these visits, a concordance is built. When the user enters a query, the
Google servers search the concordance for a list of matching Web pages and
return the list in the order of relevance.
Problem Statement
Write an application that will build a word concordance of a document. The
output from the application is an alphabetical list of all words in the given
document and the number of times they occur in the document. The docu-
ments are a text file (contents of the file are ASCII characters), and the output
of the program is saved as an ASCII file also.
Overall Plan
As usual, let’s begin the program development by first identifying the major tasks of the
program. The first task is to get a text document from a designated file. We will use a
helper class called FileManager to do this task.File processing techniques to implement
the FileManager class will be presented in Chapter 12. The whole content of an ASCII file
is represented in the program as a single String object. Using a pattern-matching tech-
nique,we extract individual words from the document.For each distinct word in the doc-
ument, we associate a counter and increment it every time the word is repeated. We will
use the second helper class called WordList for maintaining a word list.An entry in this list
has two components—a word and how many times this word occurs in the document.
wu23399_ch09.qxd 12/15/06 20:05 Page 521
9.7 Sample Development—continued
522 Chapter 9 Characters and Strings
A WordList object can handle an unbounded number of entries. Entries in the list are
arranged in alphabetical order. We will learn how to implement the WordList class in
Chapter 10.
We can express the program logic in pseudocode as
while ( the user wants to process another file ) {
Task 1: read the file;
Task 2: build the word list;
Task 3: save the word list to a file;
}
Let’s look at the three tasks and determine objects that will be responsible for
handling the tasks. For the first task, we will use the helper class FileManager. For the
second task of building a word list,we will define the Ch9WordConcordance class,whose
instance will use the Pattern and Matcher classes for word extraction, and another
helper class WordList for maintaining the word list. The last task of saving the result is
done by the FileManager class also.
Finally, we will define a top-level control object that manages all other objects.We
will call this class Ch9WordConcordanceMain. This will be our instantiable main class.
Here’s our working design document:
program
tasks
program
classes
Design Document: Ch9WordConcordanceMain
Class Purpose
Ch9WordConcordanceMain The instantiable main class of the program
that implements the top-level program
control.
Ch9WordConcordance The key class of the program.An instance of
this class manages other objects to build the
word list.
FileManager A helper class for opening a file and saving
the result to a file.Details of this class can be
found in Chapter 12.
WordList Another helper class for maintaining a word
list.Details of this class can be found in
Chapter 10.
Pattern/Matcher Classes for pattern-matching operations.
Figure 9.4 is the working program diagram.
wu23399_ch09.qxd 12/15/06 20:05 Page 522
We will implement this program in four major steps:
1. Start with a program skeleton.Define the main class with data members.To test
the main class,we will also define a skeleton Ch9WordConcordance class with
just a default constructor.
2. Add code to open a file and save the result.Extend the step 1 classes as necessary.
3. Complete the implementation of the Ch9WordConcordance class.
4. Finalize the code by removing temporary statements and tying up loose ends.
9.7 Sample Development 523
Figure 9.4 The program diagram for the Ch9WordConcordanceMain program.Base system classes
such as String and JOptionPane are not shown.
WordList
Ch9Word
Concordance
Pattern
FileManager
Matcher
Ch9Word
ConcordanceMain
A helper class
provided to us
A class we
implement
System
classes
In lieu of the Pattern and Matcher classes, we could use the String-
Tokenizer class. This class is fairly straightforward to use if the white space
(tab, return, blank, etc.) is a word delimiter.However, using this class becomes
a little more complicated if we need to include punctuation marks and
others as a word delimiter also. Overall, the Pattern and Matcher classes
are more powerful and useful in many types of applications than the String-
Tokenizer class.
development
steps
wu23399_ch09.qxd 12/15/06 20:05 Page 523
9.7 Sample Development—continued
Step 1 Development: Skeleton
The design of Ch9WordConcordanceMain is straightforward,as its structure is very sim-
ilar to that of other main classes.We will make this an instantiable main class and define
the start method that implements the top-level control logic. We will define a default
constructor to create instances of other classes. A skeleton Ch9WordConcordance class
is also defined in this step so we can compile and run the main class. The skeleton
Ch9WordConcordance class only has an empty default constructor.The working design
document for the Ch9WordConcordanceMain class is as follows:
524 Chapter 9 Characters and Strings
step 1
design
step 1 code
Design Document: The Ch9WordConcordanceMain Class
Method Visibility Purpose
constructor public Creates the instances of other classes in the
program.
start private Implements the top-level control logic of
the program.
For the skeleton, the start method loops (doing nothing inside the loop in this
step) until the user selects No on the confirmation dialog.Here’s the skeleton:
/*
Chapter 9 Sample Development: Word Concordance
File: Step1/Ch9WordConcordanceMain.java
*/
import java.util.*;
class Ch9WordConcordanceMain {
private static enum Response {YES, NO}
private FileManager fileManager;
private Ch9WordConcordance builder;
private Scanner scanner;
//----------------------------------
// Main method
//----------------------------------
public static void main(String[] args) {
Ch9WordConcordanceMain main = new Ch9WordConcordanceMain();
main.start();
}
wu23399_ch09.qxd 12/15/06 20:05 Page 524
public Ch9WordConcordanceMain() {
fileManager = new FileManager( );
builder = new Ch9WordConcordance( );
scanner = new Scanner(System.in);
}
private void start( ) {
Response userReply;
while (true) {
userReply = prompt(Run the program?);
if (userReply == Response.NO) {
break;
}
}
System.out.println(Thank you for using the program. Good-Bye);
}
private Response prompt(String question) {
String input;
Response response = Response.NO;
System.out.print(question +  (Yes - y; No - n): );
input = scanner.next();
if (input.equals(Y) || input.equals(y)) {
response = Response.YES;
}
return response;
}
}
9.7 Sample Development 525
class Ch9WordConcordance {
public Ch9WordConcordance() {
}
}
The skeleton Ch9WordConcordance class has only an empty default constructor.
Here’s the skeleton class:
wu23399_ch09.qxd 12/15/06 20:05 Page 525
9.7 Sample Development—continued
We run the program and verify that the constructor is executed correctly, and the
repetition control in the start method works as expected.
Step 2 Development: Open and Save Files
In the second development step,we add routines to handle input and output.The tasks of
opening and saving a file are delegated to the service class FileManager.We will learn the
implementation details of the FileManager class in Chapter 12. Our responsibility right
now is to use the class correctly.The class provides two key methods:one to open a file and
another to save a file.So that we can create and view the content easily,the FileManager
class deals only with text files.To open a text file, we call its openFile method.There are
two versions.With the first version,we pass the filename.For example,the code
FileManager fm = new FileManager();
String doc = ...; //assign string data
fm.saveFile(output1.txt, doc);
will save the string data doc to a file named output1.txt.With the second version,we will
let the end user select a file,using the standard file dialog.A sample file dialog is shown in
Figure 9.5.With the second version,we pass only the string data to be saved as
fm.saveFile(doc);
When there’s an error in saving a file,an IOException is thrown.
To open a text file, we use one of the two versions of the openFile method. The
distinction is identical to the one for the saveFile methods.The first version requires the
526 Chapter 9 Characters and Strings
step 1 test
step 2
design
Figure 9.5 A sample file dialog for opening a file.
wu23399_ch09.qxd 12/15/06 20:05 Page 526
filename to open.The second version allows the end user to select a file to save the data,
so we pass no parameter. The openFile method will throw a FileNotFoundException
when the designated file cannot be found and an IOException when the designated
file cannot be opened correctly.
Here’s the summary of the FileManager class:
9.7 Sample Development 527
Public Methods of FileManager
public String openFile(String filename)
throws FileNotFoundException, IOException
Opens the text file filename and returns the content as a String.
public String openFile( )
throws FileNotFoundException, IOException
Opens the text file selected by the end user,using the standard file open dialog,
and returns the content as a String.
public String saveFile(String filename, String data)
throws IOException
Save the string data to filename.
public String saveFile(String data) throws IOException
Saves the string data to a file selected by the end user,using the standard file
save dialog.
We modify the start method to open a file, create a word concordance, and then
save the generated word concordance to a file.The method is defined as follows:
private void start( ) {
Response userReply;
String document, wordList;
while (true) {
userReply = prompt(Run the program?);
if (userReply == Response.NO) {
break;
}
document = inputFile(); //open file
wordList = build(document); //build concordance
saveFile(wordList); //save the generated concordance
}
... //'Good-bye' message dialog
}
Added portion
wu23399_ch09.qxd 12/15/06 20:05 Page 527
9.7 Sample Development—continued
The inputFile method is defined as follows:
private String inputFile( ) {
String doc = ;
try {
doc = fileManager.openFile( );
} catch ( FileNotFoundException e) {
System.out.println(File not found.);
} catch ( IOException e) {
System.out.println(Error in opening file: 
+ e.getMessage());
}
System.out.println(Input Document:n + doc); //TEMP
return doc;
}
with a temporary output to verify the input routine. Because the openFile method of
FileManager throws exceptions,we handle them here with the try-catch block.
The saveFile method is defined as follows:
private void saveFile(String list) {
try {
fileManager.saveFile(list);
} catch (IOException e) {
System.out.println(Error in saving file: 
+ e.getMessage());
}
}
The method is very simple as the hard work of actually saving the text data is done by our
FileManager helper object.
Finally, the build method is defined as
private String build(String document) {
String concordance;
concordance = builder.build(document);
return concordance;
}
528 Chapter 9 Characters and Strings
wu23399_ch09.qxd 12/15/06 20:05 Page 528
The Ch9WordConcordanceMain class is now complete.To run and test this class,
we will define a stub build method for the Ch9WordConcordance class.The method is
temporarily defined as
public String build(String document) {
//TEMP
String list
= one 14ntwo 3nthree 3nfour 5nfive 92n;
return list;
//TEMP
}
We will implement the method fully in the next step.
Here’s the final Ch9WordConcordanceMain class:
9.7 Sample Development 529
step 2 code
/*
Chapter 9 Sample Development: Word Concordance
File: Step2/Ch9WordConcordanceMain.java
*/
import java.io.*;
import java.util.*;
class Ch9WordConcordanceMain {
...
private String build(String document) {
String concordance;
concordance = builder.build(document);
return concordance;
}
private String inputFile( ) {
String doc = ;
try {
doc = fileManager.openFile( );
} catch (FileNotFoundException e) {
System.out.println(File not found.);
} catch (IOException e) {
System.out.println(Error in opening file:  + e.getMessage());
}
build
inputFile
wu23399_ch09.qxd 12/15/06 20:05 Page 529
9.7 Sample Development—continued
System.out.println(Input Document:n + doc); //TEMP
return doc;
}
private void saveFile(String list) {
try {
fileManager.saveFile(list);
} catch (IOException e) {
System.out.println(Error in saving file:  + e.getMessage());
}
}
private void start( ) {
while (true) {
...
document = inputFile();
wordList = build(document);
saveFile(wordList);
}
...
}
}
530 Chapter 9 Characters and Strings
start
saveFile
The temporary Ch9WordConcordance class now has the stub build method:
class Ch9WordConcordance {
...
public String build(String document) {
//TEMP
String list = one 14ntwo 3nthree 3nfour 5nfive 92n;
return list;
//TEMP
}
}
wu23399_ch09.qxd 12/15/06 20:05 Page 530
We are ready to run the program.The step 2 directory contains several sample input
files. We will open them and verify the file contents are read correctly by checking the
temporary echo print output to System.out. To verify the output routine, we save to the
output (the temporary output created by the build method of Ch9WordConcordance)
and verify its content.Since the output is a text file,we can use any word processor or text
editor to view its contents.(Note: If we use NotePad on the Windows platform to view the
file,it may not appear correctly.See the box below on how to avoid this problem.)
9.7 Sample Development 531
step 2 test
step 3
design
The control characters used for a line separator are not the same for each plat-
form (Windows,Mac,Unix, etc.) .One platform may use n for a line separator
while another platform may use rn for a line separator. Even on the same
platform, different software may not interpret the control characters in the
same way.To make our Java code work correctly across all platforms, we do,
for example,
String newline
= System.getProperties().getProperty(line.separator);
String output = line 1 + newline + line 2 + newline;
instead of
String output = line 1nline 2n;
Step 3 Development: Generate Word Concordance
In the third development step, we finish the program by implementing the Ch9Word-
Concordance class,specifically,its build method.Since we are using another helper class
in this step, first we must find out how to use this helper class. The WordList class sup-
ports the maintenance of a word list. Every time we extract a new word from the docu-
ment, we enter this word into a word list. If the word is already in the list, its count is
incremented by 1. If the word occurs for the first time in the document, then the word
is added to the list with its count initialized to 1.When we are done processing the docu-
ment,we can get the word concordance from a WordList by calling its getConcordance
method.The method returns the list as a single String with each line containing a word
and its count in the following format:
2 Chapter
1 Early
1 However
2 In
1 already
1 also
1 an
wu23399_ch09.qxd 12/15/06 20:05 Page 531
9.7 Sample Development—continued
7 and
1 are
2 as
1 because
Because a single WordList object handles multiple documents, there’s a method
called reset to clear the word list before processing the next document. Here’s the
method summary:
532 Chapter 9 Characters and Strings
Public Methods of WordList
public void add(String word)
Increments the count for the given word.If the word is already in the list,its count
is incremented by 1.If the word does not exist in the list,then it is added to the list
with its count set to 1.
public String getConcordance( )
Returns the word concordance in alphabetical order of words as a single string.
Each line consists of a word and its count.
public void reset( )
Clears the internal data structure so a new word list can be constructed. This
method must be called every time before a new document is processed.
The general idea behind the build method of the Ch9WordConcordance class is
straightforward. We need to keep extracting a word from the document, and for every
word found,we add it to the word list.Expressed in pseudocode,we have
while (document has more words) {
word = next word in the document;
wordList.add(word);
}
String concordance = wordList.getConcordance();
The most difficult part here is how to extract words from a document. We can write
our own homemade routine to extract words, based on the technique presented in
Section 9.2. However, this is too much work to get the task done. Writing a code that
detects various kinds of word terminators (in addition to space,punctuation mark,control
characters such as tab, new line, etc., all satisfy as the word terminator) is not that easy.
Conceptually, it is not that hard, but it can be quite tedious to iron out all the details.
Instead, we can use the pattern-matching technique provided by the Pattern and
Matcher classes for a reliable and efficient solution.
The pattern for finding a word can be stated in a regular expression as
w+
wu23399_ch09.qxd 12/15/06 20:05 Page 532
Putting it in a string format results in
w+
The Pattern and Matcher objects are thus created as
Pattern pattern = Pattern.compile(w+);
Matcher matcher = pattern.matcher(document);
and the control loop to find and extract words is
wordList.reset();
while (matcher.find( )) {
wordList.add(document.substring(matcher.start(),
matcher.end()));
}
Here’s the final Ch9WordConcordance class:
9.7 Sample Development 533
step 3 code
/*
Chapter 9 Sample Development: Word Concordance
File: Step3/Ch9WordConcordance.java
*/
import java.util.regex.*;
class Ch9WordConcordance {
private static final String WORD = w+;
private WordList wordList;
private Pattern pattern;
public Ch9WordConcordance() {
wordList = new WordList();
pattern = Pattern.compile(WORD); //pattern is compiled only once
}
public String build(String document) {
Matcher matcher = pattern.matcher(document);
wordList.reset();
while (matcher.find()){
wordList.add(document.substring(matcher.start(),
matcher.end()));
}
return wordList.getConcordance();
}
}
build
wu23399_ch09.qxd 12/15/06 20:05 Page 533
9.7 Sample Development—continued
Notice how short the class is, thanks to the power of pattern matching and the
helper WordList class.
We run the program against varying types of input text files. We can use a long
document such as the term paper for the last term’s economy class (don’t forget to save it
as a text file before testing). We should also use some specially created files for testing
purposes.One file may contain only one word repeated 7 times,for example.Another file
may contain no words at all. We verify that the program works correctly for all types of
input files.
Step 4 Development: Finalize
As always, we finalize the program in the last step. We perform a critical review to find
any inconsistency or error in the methods,any incomplete methods,places to add more
comments, and so forth.
In addition, we may consider possible extensions. One is an integrated user inter-
face where the end user can view both the input document files and the output word list
files. Another is the generation of different types of list. In the sample development, we
count the number of occurrences of each word. Instead, we can generate a list of posi-
tions where each word appears in the document. The WordList class itself needs to be
modified for such extension.
534 Chapter 9 Characters and Strings
step 3 test
program
review
• The char data type represents a single character.
• The char constant is denoted by a single quotation mark, for example, ‘a’.
• The character coding scheme used widely today is ASCII (American
Standard Code for Information Exchange).
• Java uses Unicode, which is capable of representing characters of diverse
languages. ASCII is compatible with Unicode.
• A string is a sequence of characters, and in Java, strings are represented by
String objects.
• The Pattern and Matcher classes are introduced in Java 2 SDK 1.4. They
provide support for pattern-matching applications.
• Regular expression is used to represent a pattern to match (search) in a given
text.
• The String objects are immutable. Once they are created, they cannot be
changed.
• To manipulate mutable strings, use StringBuffer.
S u m m a r y
wu23399_ch09.qxd 12/15/06 20:05 Page 534
• Strings are objects in Java, and the rules for comparing objects apply when
comparing strings.
• Only one String object is created for the same literal String constants.
• The standard classes described or used in this chapter are
String Pattern
StringBuffer Matcher
StringBuilder
Exercises 535
K e y C o n c e p t s
characters
strings
string processing
regular expression
pattern matching
character encoding
String comparison
E x e r c i s e s
1. What is the difference between ’a’ and ”a”?
2. Discuss the difference between
str = str + word; //string concatenation
and
tempStringBuffer.append(word)
where str is a String object and tempStringBuffer is a StringBuffer object.
3. Show that if x and y are String objects and x == y is true, then x.equals(y) is
also true, but the reverse is not necessarily true.
4. What will be the output from the following code?
StringBuffer word1, word2;
word1 = new StringBuffer(Lisa);
word2 = word1;
word2.insert(0, Mona );
System.out.println(word1);
5. Show the state of memory after the execution of each statement in the
following code.
String word1, word2;
word1 = Hello;
word2 = word1;
word1 = Java;
wu23399_ch09.qxd 12/15/06 20:05 Page 535
6. Using a state-of-memory diagram, illustrate the difference between a null
string and an empty string—a string that has no characters in it. Show
the state-of-memory diagram for the following code. Variable word1 is a
null string, while word2 is an empty string.
String word1, word2;
word1 = null;
word2 = ;
7. Draw a state-of-memory diagram for each of the following groups of
statements.
String word1, word2; String word1, word2;
word1 = French Roast; word1 = French Roast;
word2 = word1; word2 = French Roast;
8. Write an application that reads in a character and displays the character’s
ASCII. The getText method of the JTextField class returns a String object, so
you need to extract a char value, as in
String inputString = inputField.getText();
char character = inputString.charAt(0);
Display an error message if more than one character is entered.
9. Write a method that returns the number of uppercase letters in a String object
passed to the method as an argument. Use the class method isUpperCase of
the Character class, which returns true if the passed parameter of type char is
an uppercase letter. You need to explore the Character class from the
java.lang package on your own.
10. Redo Exercise 9 without using the Character class. Hint: The ASCII of any
uppercase letter will fall between 65 (code for ’A’) and 90 (code for ’Z’).
11. Write a program that reads a sentence and prints out the sentence with all
uppercase letters changed to lowercase and all lowercase letters changed to
uppercase.
12. Write a program that reads a sentence and prints out the sentence in reverse
order. For example, the method will display
?uoy era woH
for the input
How are you?
13. Write a method that transposes words in a given sentence. For example,
given an input sentence
The gate to Java nirvana is near
the method outputs
ehT etag ot avaJ anavrin si raen
536 Chapter 9 Characters and Strings
wu23399_ch09.qxd 12/15/06 20:05 Page 536
To simplify the problem, you may assume the input sentence contains no
punctuation marks. You may also assume that the input sentence starts with a
nonblank character and that there is exactly one blank space between the
words.
14. Improve the method in Exercise 13 by removing the assumptions. For
example, an input sentence could be
Hello, how are you? I use JDK 1.2.2. Bye-bye.
An input sentence may contain punctuation marks and more than one blank
space between two words. Transposing the above will result in
olleH, woh era uoy? I esu KDJ 1.2.2. eyB-eyb.
Notice the position of punctuation marks does not change and only one
blank space is inserted between the transposed words.
15. The Ch9CountWords program that counts the number of words in a
given sentence has a bug. If the input sentence has one or more blank
spaces at the end, the value for wordCount will be 1 more than the
actual number of words in the sentence. Correct this bug in two ways:
one with the trim method of the String class and another without using
this method.
16. The Ch9ExtractWords program for extracting words in a given sentence
includes the test
if (beginIdx != endIdx) ...
Describe the type of input sentences that will result in the variables beginIdx
and endIdx becoming equal.
17. Write an application that reads in a sentence and displays the count of
individual vowels in the sentence. Use any output routine of your
choice to display the result in this format. Count only the lowercase
vowels.
Vowel counts for the sentence
Mary had a little lamb.
# of 'a' : 4
# of 'e' : 1
# of 'i' : 1
# of 'o' : 0
# of 'u' : 0
18. Write an application that determines if an input word is a palindrome. A
palindrome is a string that reads the same forward and backward, for
example, noon and madam. Ignore the case of the letter. So, for example,
maDaM, MadAm, and mAdaM are all palindromes.
Exercises 537
wu23399_ch09.qxd 12/15/06 20:05 Page 537
19. Write an application that determines if an input sentence is a palindrome, for
example, A man, a plan, a canal, Panama! You ignore the punctuation
marks, blanks, and case of the letters.
Development Exercises
For the following exercises, use the incremental development methodology to
implement the program. For each exercise, identify the program tasks, create a
design document with class descriptions, and draw the program diagram. Map
out the development steps at the start. Present any design alternatives and
justify your selection. Be sure to perform adequate testing at the end of each
development step.
20. Write an Eggy-Peggy program. Given a string, convert it to a new string by
placing egg in front of every vowel. For example, the string
I Love Java
becomes
eggI Leegoveege Jeegaveega
21. Write a variation of the Eggy-Peggy program. Implement the following four
variations:
• Sha Add sha to the beginning of every word.
• Na Add na to the end of every word.
• Sha Na Na Add sha to the beginning and na na to the end of every
word.
• Ava Move the first letter to the end of the word and add ava
to it.
Allow the user to select one of four possible variations.
22. Write a word guessing game. The game is played by two players, each
taking a turn in guessing the secret word entered by the other player. Ask the
first player to enter a secret word. After a secret word is entered, display a
hint that consists of a row of dashes, one for each letter in the secret word.
Then ask the second player to guess a letter in the secret word. If the letter is
in the secret word, replace the dashes in the hint with the letter at all
positions where this letter occurs in the word. If the letter does not appear in
the word, the number of incorrect guesses is incremented by 1. The second
player keeps guessing letters until either
• The player guesses all the letters in the word. or
• The player makes 10 incorrect guesses.
Here’s a sample interaction with blue indicating the letter entered by the player:
- - - -
S
- - - -
538 Chapter 9 Characters and Strings
wu23399_ch09.qxd 12/15/06 20:05 Page 538
A
- A - A
V
- A V A
D
- A V A
J
J A V A
Bingo! You won.
Support the following features:
• Accept an input in either lowercase or uppercase.
• If the player enters something other than a single letter (a digit, special
character, multiple letters, etc.), display an error message. The number
of incorrect guesses is not incremented.
• If the player enters the same correct letter more than once, reply with
the previous hint.
• Entering an incorrect letter the second time is counted as another
wrong guess. For example, suppose the letter W is not in the secret
word. Every time the player enters W as a guess, the number of
incorrect guesses is incremented by 1.
After a game is over, switch the role of players and continue with
another game. When it is the first player’s turn to enter a secret word, give
an option to the players to stop playing. Keep the tally and announce the
winner at the end of the program. The tally will include for each player the
number of wins and the total number of incorrect guesses made for all
games. The player with more wins is the winner. In the case where both
players have the same number of wins, the one with the lower number of
total incorrect guesses is the winner. If the total numbers of incorrect guesses
for both players are the same also, then it is a draw.
23. Write another word guessing game similar to the one described in Exercise 22.
For this word game, instead of using a row of dashes for a secret word, a
hint is provided by displaying the letters in the secret word in random order.
For example, if the secret word is COMPUTER, then a possible hint is
MPTUREOC. The player has only one chance to enter a guess. The player
wins if he guessed the word correctly. Time how long the player took to
guess the secret word. After a guess is entered, display whether the guess is
correct or not. If correct, display the amount of time in minutes and seconds
used by the player.
The tally will include for each player the number of wins and the total
amount of time taken for guessing the secret words correctly (amount of
time used for incorrect guesses is not tallied). The player with more wins
is the winner. In the case where both players have the same number of wins,
the one who used the lesser amount of time for correct guesses is the winner.
If the total time used by both players is the same also, then it is a draw.
Exercises 539
wu23399_ch09.qxd 12/15/06 20:05 Page 539
24. The word game Eggy-Peggy is an example of encryption. Encryption has
been used since ancient times to communicate messages secretly. One of the
many techniques used for encryption is called a Caesar cipher. With this
technique, each character in the original message is shifted N positions. For
example, if N  1, then the message
I d r i n k o n l y d e c a f
becomes
J ! e s j o l ! p o m z ! e f d b g
The encrypted message is decrypted to the original message by shifting back
every character N positions. Shifting N positions forward and backward is
achieved by converting the character to ASCII and adding or subtracting N.
Write an application that reads in the original text and the value for N and
displays the encrypted text. Make sure the ASCII value resulting from
encryption falls between 32 and 126. For example, if you add 8 (value of N)
to 122 (ASCII code for ‘z’), you should “wrap around” and get 35.
Write another application that reads the encrypted text and the value
for N and displays the original text by using the Caesar cipher technique.
Design a suitable user interface.
25. Another encryption technique is called a Vignere cipher. This technique is
similar to a Caesar cipher in that a key is applied cyclically to the original
message. For this exercise a key is composed of uppercase letters only.
Encryption is done by adding the code values of the key’s characters to the
code values of the characters in the original message. Code values for the
key characters are assigned as follows: 0 for A, 1 for B, 2 for C, . . . , and
25 for Z. Let’s say the key is COFFEE and the original message is I drink
only decaf. Encryption works as follows:
I d r i n k o n l y d e c a f
| | | | |
+ + + + . . . +
| | | | |
C O F F E E C O F F E E C O F F E E
K – i W . . . j
Decryption reverses the process to generate the original message. Write an
application that reads in a text and displays the encrypted text. Make sure the
ASCII value resulting from encryption or decryption falls between 32 and
126. You can get the code for key characters by (int) keyChar - 65.
Write another application that reads the encrypted text and displays the
original text, using the Vignere cipher technique.
540 Chapter 9 Characters and Strings
wu23399_ch09.qxd 12/15/06 20:05 Page 540
26. A public-key cryptography allows anyone to encode messages while only
people with a secret key can decipher them. In 1977, Ronald Rivest, Adi
Shamir, and Leonard Adleman developed a form of public-key cryptography
called the RSA system.
To encode a message using the RSA system, one needs n and e. The
value n is a product of any two prime numbers p and q. The value e is any
number less than n that cannot be evenly divided into y (that is, y  e would
have a remainder), where y  (p  1)  (q  1). The values n and e can be
published in a newspaper or posted on the Internet, so anybody can encrypt
messages. The original character is encoded to a numerical value c by using
the formula
c  me
mod n
where m is a numerical representation of the original character (for example,
1 for A, 2 for B, and so forth).
Now, to decode a message, one needs d. The value d is a number that
satisfies the formula
e  d mod y  1
where e and y are the values defined in the encoding step. The original
character m can be derived from the encrypted character c by using the
formula
m  cd
mod n
Write a program that encodes and decodes messages using the RSA system.
Use large prime numbers for p and q in computing the value for n, because
when p and q are small, it is not that difficult to find the value of d. When p
and q are very large, however, it becomes practically impossible to
determine the value of d. Use the ASCII values as appropriate for the
numerical representation of characters. Visit http://www.rsasecurity.com for
more information on how the RSA system is applied in the real world.
Exercises 541
wu23399_ch09.qxd 12/15/06 20:05 Page 541
wu23399_ch09.qxd 12/15/06 20:05 Page 542
O b j e c t i v e s
After you have read and studied this chapter,you should be able to
• Manipulate a collection of data values,using
an array.
• Declare and use an array of primitive data
types in writing a program.
• Declare and use an array of objects in writing a
program.
• Define a method that accepts an array as its
parameter and a method that returns an array.
• Describe how a two-dimensional array is
implemented as an array of arrays.
• Manipulate a collection of objects,using lists
and maps.
543
10Arrays and
Collections
wu23399_ch10.qxd 12/28/06 12:38 Page 543
eople collect all sorts of items from bottle caps to exotic cars. For proof, just go to
eBay (www.ebay.com) and see millions and millions of collectibles up for auction.
Now with computers, people amass intangible items such as music files. Exactly
how many MP3 files do you have on your computer? Probably in the hundreds and
you lost track of exactly how many. You may want to develop a custom software, so
you can store the information you want in the format you like, to keep track of all
MP3 files downloaded from the Web.
When we write a program to deal with a collection of items, say, 500 Student
objects, 200 integers, 300 MP3 files, and so forth, simple variables will not work. It
is just not practical or feasible to use 500 variables to process 500 Student objects.
In theory, you can, but honestly, do you want to type in identifiers for 500 variables
(student1, student2, . . .)? A feature supported by programming languages to ma-
nipulate a collection of values is an array.
In this chapter we will learn about Java arrays. We will learn the basics of
array manipulation and how to use different types of arrays properly and effec-
tively. In addition, we will study several collection classes from the java.util
package that provide more advanced data management features not found in the
basic Java arrays.
10.1 Array Basics
Suppose we want to compute the annual average rainfall from 12 monthly averages.
We can use three variables and compute the annual average as follows (in this and
other code fragment examples, we assume scanner is a properly declared and cre-
ated Scanner object):
double sum, rainfall, annualAverage;
sum = 0.0;
for (int i = 0; i  12; i++) {
System.out.print(Rainfall for month  + (i+1) + : );
rainfall = scanner.nextDouble();
sum += rainfall;
}
annualAverage = sum / 12.0;
Now suppose we want to compute the difference between the annual and
monthly averages for every month and display a table with three columns, similar
to the one shown in Figure 10.1.
544 Chapter 10 Arrays and Collections
I n t r o d u c t i o n
P
wu23399_ch10.qxd 12/28/06 12:38 Page 544
To compute the difference between the annual and monthly averages, we need
to remember the 12 monthly rainfall averages. Without remembering the 12 monthly
averages, we won’t be able to derive the monthly variations after the annual average
is computed. Instead of using 12 variables januaryRainfall, februaryRainfall, and so
forth to solve this problem, we use an array.
An array is a collection of data values of the same type. For example, we may
declare an array consisting of double, but not an array consisting of both int and
double. The following declares an array of double:
double[] rainfall;
The square brackets indicate the array declaration. The brackets may be attached to
a variable instead of the data type. For example, the declaration
double rainfall[];
is equivalent to the previous declaration. In Java, an array is a reference data type.
Unlike the primitive data type, the amount of memory allocated to store an array
varies, depending on the number and type of values in the array. We use the new
operator to allocate the memory to store the values in an array. Although we use the
same reserved word new for the array memory allocation as for the creation of a
new instance of a class, strictly speaking, an array is not an object.
10.1 Array Basics 545
Annual Average Rainfall: 15.03 mm
Month
1
2
3
4
5
6
7
8
9
10
11
12
Average
13.3
14.9
14.7
23.0
25.8
27.7
12.3
10.0
9.8
8.7
8.0
12.2
Variation
1.73
0.13
0.33
7.97
10.77
12.67
2.73
5.03
5.23
6.33
7.03
2.83
Figure 10.1 Monthly rainfall figures and their variation from the annual average.
array
array
declaration
In Java,an array is a reference data type.We use the new operator to allocate the
memory to store the values in an array.
wu23399_ch10.qxd 12/28/06 12:38 Page 545
The following statement allocates the memory to store 12 double values and asso-
ciates the identifier rainfall to it.
rainfall = new double[12]; //create an array of size 12
Figure 10.2 shows this array.
We can also declare and allocate memory for an array in one statement, as in
double[] rainfall = new double[12];
The number 12 designates the size of the array—the number of values the array con-
tains. We use a single identifier to refer to the whole collection and use an indexed
expression to refer to the individual values of the collection. An individual value in
an array is called an array element. Zero-based indexing is used to indicate the po-
sitions of an element in the array. They are numbered 0, 1, 2, . . . , and size – 1, where
size is the size of an array. For example, to refer to the third element of the rainfall
array, we use the indexed expression
rainfall[2]
Instead of a literal constant such as 2, we can use an expression such as
rainfall[i+3]
Notice that the index for the first position in an array is zero. As for a String
object, Java uses zero-based indexing for an array.
546 Chapter 10 Arrays and Collections
rainfall[2]
0 1 2 3 4 5 6 7 8 9 10 11
rainfall
double[] rainfall = new double[12];
This is an indexed expression
referring to the element at
position 2, that is, the third
element of the array.
Figure 10.2 An array of 12 double values.
indexed
expression
array element
The index of the first position in an array is 0.
wu23399_ch10.qxd 12/28/06 12:38 Page 546
Using the rainfall array, we can input 12 monthly averages and compute the
annual average as
double[] rainfall = new double[12];
double annualAverage,
sum = 0.0;
for (int i = 0; i  12; i++) {
System.out.print(Rainfall for month  + (i+1) + : );
rainfall[i] = scanner.nextDouble();
sum += rainfall[i];
}
annualAverage = sum / 12.0;
Figure 10.3 shows how the array will appear after all 12 values are entered.
After the 12 monthly averages are stored in the array, we can print out the
table (alignment of the columns is not done here, but will be in the complete
program listing).
double difference;
for (int i = 0; i  12; i++) {
System.out.print(i+1); //month #
//average rainfall for the month
System.out.print(  + rainfall[i]);
//difference between the monthly and annual averages
difference = Math.abs( rainfall[i] - annualAverage );
System.out.println(  + difference);
}
Here’s the complete program:
10.1 Array Basics 547
Can also be declared as
double rainfall[]
= new double[12];
rainfall[2]  28.6
24.5 32.7 18.3 12.5 24.8 9.5 4.5 5.5 12.5 24.5 27.2
0 1 2 3 4 5 6 7 8 9 10 11
28.6
rainfall
Figure 10.3 An array of 12 double values after all 12 are assigned values.
/*
Chapter 10 Sample Program: Compute the annual average rainfall
and the variation from monthly average.
File: Ch10Rainfall.java
*/
wu23399_ch10.qxd 12/28/06 12:38 Page 547
548 Chapter 10 Arrays and Collections
import java.util.*;
class Ch10Rainfall {
public static void main (String[] args) {
Scanner scanner = new Scanner (System.in);
double[] rainfall = new double[12];
double annualAverage,
sum,
difference;
sum = 0.0;
for (int i = 0; i  12; i++) {
System.out.print(Rainfall for month  + (i+1) + : );
rainfall[i] = scanner.nextDouble();
sum += rainfall[i];
}
annualAverage = sum / 12.0;
System.out.format(Annual Average Rainfall:%5.2fnn,
annualAverage);
for (int i = 0; i  12; i++) {
System.out.format(%3d, i+1); //month #
//average rainfall for the month
System.out.format(%15.2f, rainfall[i]);
//difference between the monthly and annual averages
difference = Math.abs( rainfall[i] - annualAverage );
System.out.format(%15.2fn, difference);
}
}
}
Notice that the values displayed in the columns are aligned by using the formatting
string.
An array has a public constant length for the size of an array. Using this con-
stant, we can rewrite the for loop as
for (int i = 0; i  rainfall.length; i++) {
...
}
length
wu23399_ch10.qxd 12/28/06 12:38 Page 548
This for loop is more general since we do not have to modify the loop statement when
the size of an array is changed. Also, the use of length is necessary when the size of
an array is not known in advance.This happens, for example, when we write a method
with an array as its parameter. We will provide an example of such a method in
Section 10.3.
Notice the prompts for getting the values in the previous example are Rainfall
for month 1, Rainfall for month 2, and so forth. A better prompt will spell out the
month name, for example, Rainfall for January, Rainfall for February, and so forth.
We can easily achieve a better prompt by using an array of strings. Here’s how:
double[] rainfall = new double[12]; //an array of double
String[] monthName = new String[12]; //an array of String
double annualAverage,
sum = 0.0;
monthName[0] = January;
monthName[1] = February;
monthName[2] = March;
monthName[3] = April;
monthName[4] = May;
monthName[5] = June;
monthName[6] = July;
monthName[7] = August;
monthName[8] = September;
monthName[9] = October;
monthName[10] = November;
monthName[11] = December;
for (int i = 0; i  rainfall.length; i++) {
System.out.print(Rainfall for month +
monthName[i] + : );
rainfall[i] = scanner.nextDouble();
sum += rainfall[i];
}
annualAverage = sum / 12.0;
10.1 Array Basics 549
It is very easy to mix up the length value of an array and the length method of a
String object.The length is a method for a String object,so we use the syntax for
calling a method.
String str = This is a string;
int size = str.length();
But for an array,which is not an object but a reference data type,we do not use the
syntax of method calling.We refer to the length value as
int size = rainfall.length;
wu23399_ch10.qxd 12/28/06 12:38 Page 549
Instead of assigning array elements individually, we can initialize the array at
the time of declaration. We can, for example, initialize the monthName array by
String[] monthName = { January, February, March,
April, May, June, July,
August, September, October,
November, December };
Notice that we do not specify the size of an array if the array elements are initialized
at the time of declaration. The size of an array is determined by the number of val-
ues in the list. In the above example, there are 12 values in the list, so the size of the
array monthName is set to 12.
Let’s try some more examples. We assume the rainfall array is declared, and
all 12 values are read in. The following code computes the average rainfall for the
odd months (January, March, . . .) and the even months (February, April, . . .).
double oddMonthSum, oddMonthAverage,
evenMonthSum, evenMonthAverage;
oddMonthSum = 0.0;
evenMonthSum = 0.0;
//compute the average for the odd months
for (int i = 0; i  rainfall.length; i += 2) {
oddMonthSum += rainfall[i];
}
oddMonthAverage = oddMonthSum / 6.0;
//compute the average for the even months
for (int i = 1; i  rainfall.length; i += 2) {
evenMonthSum += rainfall[i];
}
evenMonthAverage = evenMonthSum / 6.0;
We can compute the same result by using one for loop.
for (int i = 0; i  rainfall.length; i += 2 ) {
oddMonthSum += rainfall[i];
evenMonthSum += rainfall[i+1];
}
oddMonthAverage = oddMonthSum / 6.0;
evenMonthAverage = evenMonthSum / 6.0;
To compute the average for each quarter (quarter 1 has January, February, and
March; quarter 2 has April, May, and June; and so forth), we can write
for (int i = 0; i  3; i++ ) {
quarter1Sum += rainfall[i];
quarter2Sum += rainfall[i+3];
quarter3Sum += rainfall[i+6];
quarter4Sum += rainfall[i+9];
}
550 Chapter 10 Arrays and Collections
No size is specified.
wu23399_ch10.qxd 12/28/06 12:38 Page 550
quarter1Average = quarter1Sum / 3.0;
quarter2Average = quarter2Sum / 3.0;
quarter3Average = quarter3Sum / 3.0;
quarter4Average = quarter4Sum / 3.0;
We can use another array to store the quarter averages instead of using four variables:
double[] quarterAverage = new double[4];
for (int i = 0; i  4; i++) {
sum = 0;
for (int j = 0; j  3; j++) { //compute the sum of
sum += rainfall[3*i + j]; //one quarter
}
quarterAverage[i] = sum / 3.0;//average for quarter i+1
}
Notice how the inner for loop is used to compute the sum of one quarter. The fol-
lowing table illustrates how the values for the variables i and j and the expression
3*i + j change.
10.1 Array Basics 551
i j 3*i + j
0 0 0
1 1
2 2
1 0 3
1 4
2 5
2 0 6
1 7
2 8
3 0 9
1 10
2 11
/*
Chapter 10 Sample Program: Compute different statistics
from monthly rainfall averages.
File: Ch10RainfallStat.java
*/
Here’s the complete program:
wu23399_ch10.qxd 12/28/06 12:38 Page 551
552 Chapter 10 Arrays and Collections
import java.util.*;
class Ch10RainfallStat {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String[] monthName = { January, February, March,
April, May, June, July,
August, September, October,
November, December };
double[] rainfall = new double[12];
double[] quarterAverage = new double[4];
double annualAverage,
sum,
difference;
double oddMonthSum, oddMonthAverage,
evenMonthSum, evenMonthAverage;
sum = 0.0;
for (int i = 0; i  rainfall.length; i++) {
System.out.print(Rainfall for month  + monthName[i] + : );
rainfall[i] = scanner.nextDouble();
sum += rainfall[i];
}
annualAverage = sum / 12.0;
System.out.format( Annual Average Rainfall:%6.2fnn,
annualAverage );
oddMonthSum = 0.0;
evenMonthSum = 0.0;
///////////// Odd and Even Month Averages //////////////////
//compute the average for the odd months
for (int i = 0; i  rainfall.length; i += 2) {
oddMonthSum += rainfall[i];
}
oddMonthAverage = oddMonthSum / 6.0;
//compute the average for the even months
for (int i = 1; i  rainfall.length; i += 2) {
evenMonthSum += rainfall[i];
}
wu23399_ch10.qxd 12/28/06 12:38 Page 552
10.1 Array Basics 553
evenMonthAverage = evenMonthSum / 6.0;
System.out.format(Odd Month Rainfall Average: %6.2fn,
oddMonthAverage );
System.out.format(Even Month Rainfall Average:%6.2fnn,
evenMonthAverage );
/////////////////// Quarter Averages //////////////////////
for (int i = 0; i  4; i++) {
sum = 0;
for (int j = 0; j  3; j++) { //compute the sum of
sum += rainfall[3*i + j]; //one quarter
}
quarterAverage[i] = sum / 3.0; //average for quarter i+1
System.out.format(Rainfall Average Qtr.%3d:%6.2fn,
i+1, quarterAverage[i] );
}
}
}
In the previous examples, we used a constant to specify the size of an array,
such as the literal constant 12 in the following declaration:
double[] rainfall = new double[12];
Using constants to declare array sizes does not always lead to efficient space usage.
We call the declaration of arrays with constants a fixed-size array declaration. There
are two potential problems with fixed-size array declarations. Suppose, for exam-
ple, we declare an integer array of size 100:
int[] number = new int[100];
The first problem is that the program can process only up to 100 numbers. What
if we need to process 101 numbers? We have to modify the program and compile
it again. The second problem is a possible underutilization of space. The above de-
claration allocates 100 spaces whether they are used or not. Suppose the program
on average processes 20 numbers. Then the program’s average space usage is
only 20 percent of the allocated space. With Java, we are not limited to fixed-size
array declaration. We can declare an array of different size every time we run the
fixed-size array
declaration
wu23399_ch10.qxd 12/28/06 12:38 Page 553
program. The following code prompts the user for the size of an array and de-
clares an array of designated size:
int size;
int[] number;
System.out.print(Size of an array: );
size = scanner.nextInt();
number = new int[size];
With this approach, every time the program is executed, only the needed
amount of space is allocated for the array. Any valid integer arithmetic expression
is allowed for size specification, for example,
System.out.print(Enter int: );
size = scanner.nextInt();
number = new int[size*size + 2* size + 5];
We call the creation of arrays with nonconstant values a variable-size array creation.
This capability comes very handy, for example, when an array runs out of space and
we need to create a new, larger array. Suppose we start with an array of 20 elements.
What would happen when we had to add the 21st element? We can create a new,
larger array, say, twice as large as the original array. We then copy the values from
the original array to the new array and finally add the 21st element to the new array.
We will show you a concrete example of this scenario in Section 10.7.
554 Chapter 10 Arrays and Collections
variable-size
array creation
Notice the first index position of an array is 0. Java adopted this feature from the
programming language C. Using the zero-based indexing, the index value of an
element indicates the number of elements in front of the element.For example,an
index value of 0 for the first element indicates that there are zero elements in front
of it;an index value of 4 for the fifth element indicates that there are four elements
in front of it.Zero-based indexing allows a simpler formula to compute the actual
memory address of array elements.
1. Which of the following statements are invalid?
a. float number[23];
b. float number = { 1.0f, 2.0f, 3.0f };
c. int number;
number = new Array[23];
d. int[] number = [ 1, 2, 3, 4 ];
wu23399_ch10.qxd 12/28/06 12:38 Page 554
2. Write a code fragment to compute the sum of all positive real numbers stored
in the following array.
double[] number = new double[25];
3. Describe the difference between the following two code fragments.
//code fragment 1
for (int i = 0; i  number.length; i++) {
if ( i % 2 == 0 ) {
System.out.println( number[i] );
}
}
//code fragment 2
for (int i = 0; i  number.length; i++) {
if ( number[i] % 2 == 0 ) {
System.out.println( number[i] );
}
}
10.2 Arrays of Objects
Array elements are not limited to primitive data types. Indeed, since a String is an
object, we actually have seen an example of an array of objects in Section 10.1. In
this section we will explore arrays of objects. To illustrate the processing of an array
of objects, we will use the Person class in the following examples. We will define
this Person class later in the chapter to introduce additional object-oriented concepts.
Here’s the portion of the Person class definition we will use in this section:
10.2 Arrays of Objects 555
Public Methods of the Person Class
public int getAge ( )
Returns the age of a person.Default age of a person is set to 0.
public char getGender( )
Returns the gender of a person.The character F stands for female and M for male.
Default gender of a person is set to the character U for unknown.
public String getName ( )
Returns the name of a person.Default name of a person is set to Not Given.
public void setAge ( int age )
Sets the age of a person.
public void setGender( char gender )
Sets the gender of a person to the argument gender.The character F stands for
female and M for male.The character U designates unknown gender.
public void setName ( String name )
Sets the name of a person to the argument name.
wu23399_ch10.qxd 12/28/06 12:38 Page 555
The following code creates a Person object:
Person latte;
latte = new Person( );
latte.setName(Ms. Latte);
latte.setAge(20);
latte.setGender('F');
System.out.println( Name:  + latte.getName() );
System.out.println( Age :  + latte.getAge() );
System.out.println( Sex :  + latte.getGender() );
Now let’s study how we can create and manipulate an array of Person objects.
An array of objects is declared and created just as an array of primitive data types
is. The following are a declaration and a creation of an array of Person objects.
Person[] person; //declare the person array
person = new Person[20]; //and then create it
Execution of the above code will result in a state shown in Figure 10.4.
Notice that the elements, that is, Person objects, are not yet created; only the
array is created. Array elements are initially null. Since each individual element is
an object, it also must be created. To create a Person object and set it as the array’s
first element, we write
person[0] = new Person( );
Figure 10.5 shows the state after the first Person object is added to the array.
Notice that no data values are assigned to the object yet. The object has default
values at this point. To assign data values to this object, we can execute
person[0].setName ( Ms. Latte );
person[0].setAge ( 20 );
person[0].setGender( 'F' );
The indexed expression
person[0]
556 Chapter 10 Arrays and Collections
Figure 10.4 An array of Person objects after the array is created.
0 1 2 3 4
•••
16 17 18 19
person
- null
Person[] person;
person = new Person[20];
wu23399_ch10.qxd 12/28/06 12:38 Page 556
refers to the first object in the person array. Since this expression refers to an object,
we write
person[0].setAge( 20 );
to call this Person object’s setAge method, for example. This is the syntax we use to
call an object’s method. We are just using an indexed expression to refer to an ob-
ject instead of a simple variable.
Let’s go through typical array processing to illustrate the basic operations.
The first is to create Person objects and set up the person array. We assume that the
person array is already declared and created.
String name, inpStr;
int age;
char gender;
for (int i = 0; i  person.length; i++) {
//read in data values
System.out.print(Enter name: );
name = scanner.next();
System.out.print(Enter age: );
age = scanner.nextInt();
System.out.print(Enter gender: );
inpStr = scanner.next();
gender = inpStr.charAt(0);
//create a new Person and assign values
person[i] = new Person( );
person[i].setName ( name );
person[i].setAge ( age );
person[i].setGender( gender );
}
10.2 Arrays of Objects 557
Figure 10.5 The person array with one Person object added to it.
0 1 2 3 4
•••
16 17 18 19
person
:Person
Not Given
0
U
No values are assigned
yet, so this object has
default values.
wu23399_ch10.qxd 12/28/06 12:38 Page 557
Note: To focus on array processing, we used the most simplistic input routine. For
instance, we did not perform any input error checking, but this is not to say that input
error checking is unimportant. We simply want to focus on array processing here.
To find the average age, we execute
double sum = 0, averageAge;
for (int i = 0; i  person.length; i++) {
sum += person[i].getAge();
}
averageAge = sum / person.length;
To print out the name and age of the youngest and the oldest persons, we can
execute
String nameOfYoungest, nameOfOldest;
int min, max, age;
nameOfYoungest = nameOfOldest = person[0].getName();
min = max = person[0].getAge();
for (int i = 1; i  person.length; i++) {
age = person[i].getAge();
if (age  min) { //found a younger person
min = age;
nameOfYoungest = person[i].getName();
}else if (age  max) { //found an older person
max = age;
nameOfOldest = person[i].getName();
}
}
System.out.println(Oldest :  + nameOfOldest +  is 
+ max +  years old.);
System.out.println(Youngest:  + nameOfYoungest +  is 
+ min +  years old.);
Instead of using separate String and int variables, we can use the index to the
youngest and the oldest persons. Here’s the code:
int minIdx, //index to the youngest person
maxIdx; //index to the oldest person
minIdx = maxIdx = 0;
for (int i = 1; i  person.length; i++) {
if (person[i].getAge()  person[minIdx].getAge()) {
//found a younger person
minIdx = i;
558 Chapter 10 Arrays and Collections
find the
average age
find the
youngest and
the oldest
persons
wu23399_ch10.qxd 12/28/06 12:38 Page 558
10.2 Arrays of Objects 559
Figure 10.6 An array of Person objects with two Person variables.
0 1 2 3 4
•••
••• •••
16 17 18 19
person
:Person
Jane
87
F
:Person
Latte
20
F
•••
oldest youngest
}else if (person[i].getAge()  person[maxIdx.getAge()){
//found an older person
maxIdx = i;
}
}
System.out.println(Oldest :  + person[maxIdx].getName()
+  is 
+ person[maxIdx].getAge()
+  years old.);
System.out.println(Youngest:  + person[minIdx].getName()
+  is 
+ person[minIdx].getAge()
+  years old.);
Yet another approach is to use variables for Person objects. Figure 10.6 shows
how the Person variables oldest and youngest point to objects in the person array.
Here’s the code using Person variables:
Person youngest, //points to the youngest person
oldest; //points to the oldest person
youngest = oldest = person[0];
for (int i = 1; i  person.length; i++) {
if (person[i].getAge()  youngest.getAge()) {
//found a younger person
youngest = person[i];
}
wu23399_ch10.qxd 12/28/06 12:38 Page 559
else if (person[i].getAge()  oldest.getAge()) {
//found an older person
oldest = person[i];
}
}
System.out.println(Oldest :  + oldest.getName()
+  is  + oldest.getAge() +  years old.);
System.out.println(Youngest:  + youngest.getName()
+  is  + youngest.getAge() +  years old.);
Our next example is to search for a particular person. We can scan through the
array until the desired person is found. Suppose we want to search for a person
whose name is Latte. If we assume the person is in the array, then we can write
int i = 0;
while (!person[i].getName().equals(Latte)) {
i++;
}
System.out.println(Found Ms. Latte at position  + i);
The expression
person[i].getName().equals(Latte)
is evaluated left to right and is equivalent to
Person p = person[i];
String str= p.getName();
str.equals(Latte);
In this example, we assume that the person for whom we are searching is in
the array. If we cannot assume this, then we need to rewrite the terminating condi-
tion to take care of the case when the person is not in the array. Here’s how:
int i = 0;
while (i  person.length //still more persons to search
!person[i].getName().equals(Latte)) {
i++;
}
if (i == person.length) {
//not found - unsuccessful search
System.out.println(Ms. Latte was not in the array);
} else {
//found - successful search
System.out.println(Found Ms. Latte at position  + i);
}
560 Chapter 10 Arrays and Collections
find a particu-
lar person
wu23399_ch10.qxd 12/28/06 12:38 Page 560
Here’s the complete program that summarizes the topics covered so far in this
section:
10.2 Arrays of Objects 561
/*
Chapter 10 Sample Program: Illustrate the processing
of an array of Person objects
File: Ch10ProcessPersonArray.java
*/
import java.util.*;
class Ch10ProcessPersonArray {
public static void main (String[] args) {
Person[] person; //declare the person array
person = new Person[5]; //and then create it
//----------- Create person Array -------------------//
String name, inpStr;
int age;
char gender;
for (int i = 0; i  person.length; i++) {
//read in data values
System.out.print(Enter name: );
name = scanner.next();
System.out.print(Enter age: );
age = scanner.nextInt();
System.out.print(Enter gender: );
inpStr = scanner.next();
gender = inpStr.charAt(0);
//create a new Person and assign values
person[i] = new Person( );
person[i].setName ( name );
person[i].setAge ( age );
person[i].setGender( gender );
}
//-------------- Compute Average Age --------------//
float sum = 0, averageAge;
for (int i = 0; i  person.length; i++) {
sum += person[i].getAge();
}
wu23399_ch10.qxd 12/28/06 12:38 Page 561
averageAge = sum / (float) person.length;
System.out.println(Average age:  + averageAge);
System.out.println(n);
//------ Find the youngest and oldest persons ----------//
//------ Approach No. 3: Using person reference --------//
Person youngest, //points to the youngest person
oldest; //points to the oldest person
youngest = oldest = person[0];
for (int i = 1; i  person.length; i++) {
if (person[i].getAge()  youngest.getAge()) {
//found a younger person
youngest = person[i];
}
else if (person[i].getAge()  oldest.getAge()) {
//found an older person
oldest = person[i];
}
}
System.out.println(Oldest :  + oldest.getName()
+  is  + oldest.getAge() +  years old.);
System.out.println(Youngest:  + youngest.getName()
+  is  + youngest.getAge() +  years old.);
//----------- Search for a particular person ------------//
System.out.print(Name to search: );
String searchName = scanner.next();
int i = 0;
while (i  person.length  //still more persons to search
!person[i].getName().equals(searchName)) {
i++;
}
if (i == person.length) {
//not found - unsuccessful search
System.out.println( searchName +  was not in the array );
} else {
//found - successful search
System.out.println(Found  + searchName +  at position  + i);
}
}
}
562 Chapter 10 Arrays and Collections
wu23399_ch10.qxd 12/28/06 12:38 Page 562
Now let’s consider the deletion operation. The deletion operation requires some
kind of a search routine to locate the Person object to be removed. To concentrate on
the deletion operation, we will assume there’s a search method that returns the index
of the Person object in the array to be removed.There are two possible ways to remove
an object from the array. The first approach is to reset the array element to null. Re-
member that each element in an array of objects is a reference to an object, so remov-
ing an object from an array could be accomplished by setting the reference to null.
Figure 10.7 illustrates how the object at position 1 is deleted by using approach 1.
Since any index position can be set to null, there can be “holes,” that is, null
references, anywhere in the array. Instead of intermixing real and null references,
the second approach will pack the elements so that the real references occur at the
beginning and the null references at the end:
10.2 Arrays of Objects 563
Figure 10.7 Approach 1 deletion:setting a reference to null. The array length is 4.
0 1
Before
Remove this
2 3
person
0 1
After
2 3
person
int delIdx = 1;
person[delIdx] = null;
A Person object at index 1
is removed.
A
:Person
C
:Person
D
:Person
A B C D
:Person :Person :Person :Person
delete a
particular
person
Real references null references
0 1 2
person
17
i+1
i
••• •••
18 19
With approach 2, we must fill the hole. There are two possible solutions. The
first solution is to pack the elements. If an object at position J is removed (i.e., this po-
sition is set to null), then elements from position J+1 up to the last non-null reference
are shifted one position lower. And, finally, the last non-null reference is set to null.
The second solution is to replace the removed element by the last element in the
wu23399_ch10.qxd 12/28/06 12:38 Page 563
array. The first solution is necessary if the Person objects are arranged in some order
(e.g., in ascending order of age). The second solution is a better one if the Person ob-
jects are not arranged in any order. Since we are not arranging them in any order, we
will use the second solution. Figure 10.8 illustrates how the object at position 1 is
replaced by the last element.
The search routine we presented earlier in this section assumes the full array;
that is, all elements are non-null references. With the deletion routine, either ap-
proach 1 or 2, given above, an array element could be a null. The search routine
must therefore be modified to skip the null references (for approach 1) or to stop the
search when the first null reference is encountered (for approach 2).
In both Figures 10.7 and 10.8, we removed the icon for Person B in the diagrams
when the array element was set to null as though the object were erased from the
memory. Eventually, the object will indeed be erased, but the operation of assigning
null to the array element will not erase the object by itself. The operation simply
initiates a chain reaction that will eventually erase the object from the memory.
As we have shown several times already, a single object can have multiple ref-
erences pointing to it. For example, the following code will result in two references
pointing to a single Person object:
Person p1, p2;
p1 = new Person();
p2 = p1;
p1 p2
:Person
564 Chapter 10 Arrays and Collections
0 1
Before
2 3
person
0 1
After
2 3
person
int delIdx = 1;
int last = 3;
person[delIdx] = person[last];
person[last] = null;
A Person object at index 1 is removed,
and the last object at index 3 replaces
the removed object.
A B C D
:Person :Person :Person :Person
A
:Person
C
:Person
D
:Person
Remove this
Figure 10.8 Approach 2 deletion:replace the removed element with the last element in the array. The array
length is 4.
wu23399_ch10.qxd 12/28/06 12:38 Page 564
When an object has no references pointing to it, then the system will erase the object
and make the memory space available for other uses. We call the erasing of an
object deallocation of memory, and the process of deallocating memory is called
garbage collection. Unlike in other programming languages, garbage collection is
automatically done in Java, so we do not have to be conscious of it when develop-
ing Java programs.
10.3 The For-Each Loop 565
garbage
collection
1. Which of these statements are invalid?
a. Person[25] person;
b. Person[ ] person;
c. Person person[] = new Person[25];
d. Person person[25] = new Person[25];
2. Write a code fragment to print out the names of those who are older than 20.
Assume the following declaration and that the array is already set up
correctly.
Person[ ] friend = new Person[100];
10.3 The For-Each Loop
In Chapter 6, we mentioned a new form of the for loop that is introduced in Java 5.0.
There is no official name to this for loop, but the term for-each is used most often.
The term enhanced for loop is also used by many to refer to this for loop. We will
use both terms interchangeably in this book.
We will show here how to use the for-each loop in processing an array. We will
show how to use it in processing a collection in Section 10.5. Let’s assume number
is an int array of 100 integers. Using the standard for loop, we compute the sum of
all elements in the number array as follows:
int sum = 0;
for (int i = 0; i  number.length; i++) {
sum = sum + number[i];
}
We can also compute the sum by using a for-each loop as follows:
int sum = 0;
for (int value : number) {
sum = sum + value;
}
wu23399_ch10.qxd 12/28/06 12:38 Page 565
The loop iterates over every element in the number array, and the loop body is
executed for each iteration. The variable value refers to each element in the array
during the iteration. So we can interpret this loop as saying something like “For
each value in number, execute the following loop body.”
The general syntax for the for-each loop is
for ( type variable : array )
loop body
where type is the data type of variable, array the name of the array, and
loop body is a sequence of 0 or more statements (the left and right braces are
required if there is more than one statement in the loop body).
Let’s look at another example. This time we use an array of objects. Suppose
we have an array of 100 Person objects called person:
Person[] person = new Person[100];
The Person class is defined in Section 10.2. Assuming that 100 Person objects are
created and assigned to person[0] to person[99], we can list the name of every
person in the array by using the following for-each loop:
for (Person p : person) {
System.out.println(p.getName());
}
Contrast this to the standard for loop:
for (int i = 0; i  person.length; i++) {
System.out.println(person[i].getName());
}
The for-each loop is, in general, cleaner and easier to read.
There are several restrictions on using the for-each loop. First, you cannot
change an element in the array during the iteration. The following code does not
reset the array elements to 0:
int [] number = {10, 20, 30, 40, 50};
for (int value : number){
value = 0;
}
for (int value : number) {
System.out.println(value);
}
566 Chapter 10 Arrays and Collections
This loop has no effect.
wu23399_ch10.qxd 12/28/06 12:38 Page 566
The first for-each loop has no effect, so the output from this code will be
10
20
30
40
50
We can characterize the for-each loop as a read-only iteration of the elements.
10.3 The For-Each Loop 567
The for-each loop only allows access to the elements. The elements cannot
be changed.
For an array of objects, an element is actually a reference to an object, so the fol-
lowing for-each loop is ineffective. Specifically, it does not reset the elements to null.
Person[] person = new Person[100];
for (int i = 0; i  person.length; i++) {
person[i] = ...; //code to create a new Person object
}
for (Person p : person) {
p = null;
}
Although we cannot change the elements of an array, we can change the
content of an object if the element is a reference to an object. For example, the
following for-each loop will reset the names of all objects to Java:
Person[] person = new Person[100];
for (int i = 0; i  person.length; i++) {
person[i] = ...; //code to create a new Person object
}
for (Person p : person) {
p.setName(Java);
}
Notice that we are not changing the elements (references to objects) themselves, but
the content of the objects referenced by these elements. Thus, the code is effective.
This loop is effective. The
name of every Person
object is set to Java.
This loop has no effect.
wu23399_ch10.qxd 12/28/06 12:38 Page 567
The second restriction is that we cannot access more than one array using a
single for-each loop. Suppose we have two integer arrays num1 and num2 of length
200 and want to create a third array num3 whose elements are sum of the corre-
sponding elements in num1 and num2. Here’s the standard for loop:
int[] num1 = new int[200];
int[] num2 = new int[200];
int[] num3 = new int[200];
//code to assign values to the elements of num1 and num2
//compute the sums
for (int i = 0; i  num3.length; i++) {
num3[i] = num1[i] + num2[i];
}
Such a loop cannot be written with a for-each loop.
568 Chapter 10 Arrays and Collections
The for-each loop allows access to only a single array.
The third restriction is that we must access all elements in an array from the
first to the last element. We cannot, for example, access only the first half or the last
half of the array. We cannot access elements in reverse order either.
The for-each loop iterates over every element of an array from the first to the last
element. We cannot use the for-each loop to access only a portion of an array or
to access the elements in reverse order.
This restriction complicates the matter when we try to access elements in an
array of objects. Consider the following code:
Person[] person = new Person[100];
for (int i = 0; i  50; i++) {
person[i] = ...; //code to create a new Person object
}
wu23399_ch10.qxd 12/28/06 12:38 Page 568
10.4 Passing Arrays to Methods 569
1. Rewrite the following for loop by using a for-each loop.
for (int i = 0; i  number.length; i++) {
System.out.println(number[i]);
}
2. Rewrite the following for loop by using the standard for loop.
for (Person p : person) {
System.out.println(p.getName());
}
3. Why can’t the following for loop be expressed as a for-each loop?
for (int i = 0; i  number.length; i++) {
number[i] = number[i] + 50;
}
10.4 Passing Arrays to Methods
We discussed the passing of an object to a method by using String objects as illus-
trations in Chapter 4. Since both an array and an object are a reference data type, the
rules for passing an object to a method and returning an object from the method
apply to arrays also. However, there are some additional rules we need to remember
in passing an array to a method and returning it from a method. We will cover these
topics in this section.
Let’s define a method that returns the index of the smallest element in an array
of real numbers. The array to search for the smallest element is passed to the
method. Here’s the method:
public int searchMinimum(double[] number) {
int indexOfMinimum = 0;
for (int i = 1; i  number.length; i++) {
if (number[i]  number[indexOfMinimum]) { //found a
for (Person p : person) {
System.out.println(
p.getName());
}
This code will crash when the variable p is set to the 51st element (i.e., an element
at index position 50), because the element is null. Notice that only the first 50 ele-
ments actually point to Person objects. The elements in the second half of the array
are all null.
This loop will result in a
NullPointerException error.
wu23399_ch10.qxd 12/28/06 12:38 Page 569
indexOfMinimum = i; //smaller element
}
}
return indexOfMinimum;
}
Notice that we use the square brackets to designate that number is an array. The
square brackets may also be attached to the parameter, as in
public int searchMinimum(double number[])
To call this method (from a method of the same class), we write something
like this:
double[] arrayOne, arrayTwo;
//create and assign values to arrayOne and arrayTwo
...
//get the index of the smallest element of arrayOne
int minOne = searchMinimum( arrayOne );
//get the index of the smallest element of arrayTwo
int minTwo = searchMinimum( arrayTwo );
//output the result
System.out.print(Minimum value in Array One is );
System.out.print(arrayOne[minOne] + at position 
+ minOne);
System.out.print(nn);
System.out.print(Minimum value in Array Two is );
System.out.print(arrayTwo[minTwo] +  at position 
+ minTwo);
Just like other objects, an array is a reference data type, so we are passing the
reference to an array, not the whole array, when we call the searchMinimum method.
For example, when the method is called with arrayOne as its argument, the states of
memory illustrated in Figures 10.9 and 10.10 will result. There are two references
to the same array. The method does not create a separate copy of the array.
570 Chapter 10 Arrays and Collections
When an array is passed to a method,only its reference is passed.A copy of the array
is not created in the method.
Now let’s try another example in which we return an array (actually the refer-
ence to the array) from a method. Suppose we want to define a method that inputs
wu23399_ch10.qxd 12/28/06 12:38 Page 570
10.4 Passing Arrays to Methods 571
double values and returns the values as an array of double. We can define the
method as follows:
public double[] readDoubles() {
double[] number;
System.out.print(How many input values? );
int N = scanner.nextInt();
number = new double[N];
for (int i = 0; i  N; i++) {
System.out.print(Number  + i + : );
number[i] = scanner.nextDouble();
}
return number;
}
arrayOne
1 execution flow
Local variables do not exist
before the method execution.
minOne = searchMinimum(arrayOne);
public int searchMinimum(double[] number) {
...
}
at before calling searchMinimum
1
1
state of memory
•••
2
Memory space for the parameter of searchMinimum
is allocated, and the value of arrayOne, which is a
reference (address) to an array, is copied to
number. So now both variables refer to the same
array.
minOne = searchMinimum(arrayOne);
public int searchMinimum(double[] number) {
...
}
at after the parameter is assigned
2
2
arrayOne number
•••
Figure 10.9 Passing an array to a method means we are passing a reference to an array. We are not passing
the whole array.
wu23399_ch10.qxd 12/28/06 12:38 Page 571
The square brackets beside the method return type double indicate that the
method returns an array of double. Because an array is a reference data type, when
we say “returns an array of double,” we are really saying “returns the reference to
an array of double.” We will use the shorter expression in general and use the longer
expression only when we need to be precise.
The readDoubles method is called in this manner:
double[] arrayOne, arrayTwo;
//assign values to arrayOne and arrayTwo
arrayOne = readDoubles();
arrayTwo = readDoubles();
572 Chapter 10 Arrays and Collections
arrayOne
4
minOne = searchMinimum(arrayOne);
public int searchMinimum(double[] number) {
...
}
at after searchMinimum
4
4
•••
3
After the sequence of activities,
before returning from the method.
Memory space for searchMinimum is deallocated upon
exiting the method. The array itself is not part of
memory allocated for searchMinimum and will not be
deallocated upon exit.
minOne = searchMinimum(arrayOne);
public int searchMinimum(double[] number) {
...
}
at before return
3
3
Note: The searchMinimum method did not make any
changes to the contents of the array. However, if it did,
then the changes made to the array contents will remain
in effect because the array is not deallocated.
arrayOne number
•••
Figure 10.10 Continuation of Figure 10.9.
wu23399_ch10.qxd 12/28/06 12:38 Page 572
Bad Version
Since a new array is created by the method, we do not have to create an array from
the calling side. In other words, we don’t have to do this:
double[] arrayOne, arrayTwo;
arrayOne = new double[30]; //this is NOT necessary
arrayOne = readDoubles();
It won’t cause an error if we create an array from the calling side, but we are doing
a very wasteful operation. First, it takes up extra memory space. Second, it slows
down the whole operation because the computer must garbage-collect the extra
memory space that is not being used.
Let’s try an alternative approach. This time, instead of creating an array inside
the method and returning the array, the calling side creates an array and passes this
array to the method:
int[] myIntArray = new int[50];
readIntegers(myIntArray);
The method readIntegers fills the passed array with integers. The method is defined
as follows:
public void readIntegers(int[] number) {
for (int i = 0; i  number.length; i++) {
System.out.print(Number  + i + : );
number[i] = scanner.nextDouble();
}
}
Notice the return type of readIntegers is void because we are not returning an array.
The method modifies the array that is passed to it.
Be careful not to mix the two alternative approaches. The following method
will not work:
public void badMethod( double[] number ) {
System.out.print(How many input values? );
int N = scanner.nextInt();
number = new double[N];
for (int i = 0; i  N; i++) {
System.out.print(Number  + i + : );
number[i] = scanner.nextDouble();
}
}
10.4 Passing Arrays to Methods 573
wu23399_ch10.qxd 12/28/06 12:38 Page 573
Code such as
double[] arrayOne = new double[30];
badMethod( arrayOne );
will leave arrayOne unchanged. Figures 10.11 and 10.12 show the effect of creating
a local array in badMethod and not returning it. (Note: The return type of bad-
Method is void.)
574 Chapter 10 Arrays and Collections
arrayOne
1 Execution flow
Local variables do not exist
before the method execution.
badMethod(arrayOne);
badMethod(arrayOne);
public void badMethod(double[] number) {
...
number = new double[N];
...
}
public void badMethod(double[] number) {
...
number = new double[N];
...
}
at before calling searchMinimum
1
1
state of memory
•••
2
Memory space for the parameter of badMethod
is allocated, and the value of arrayOne, which is a
reference (address) to an array, is copied to
number. So now both variables refer to the same
array.
at after the parameter is assigned
2
2
arrayOne number
•••
Figure 10.11 Effect of creating a local array and not returning it.
1. What will be an output from the following code?
int[] list = {10, 20, 30, 40 };
myMethod(list);
System.out.println(list[1]);
wu23399_ch10.qxd 12/28/06 12:38 Page 574
System.out.println(list[3]);
...
public void myMethod(int[] intArray) {
for (int i = 0; i  intArray.length; i+=2) {
intArray[i] = i;
}
}
2. If we replace myMethod of question 1 with the following, what will be an output?
public void myMethod(int[] intArray)
{
int[] local = intArray;
for (int i = 0; i  local.length; i+=2) {
local[i] = i;
}
}
10.4 Passing Arrays to Methods 575
badMethod(arrayOne);
public void badMethod(double[] number) {
...
number = new double[N];
...
}
3
A separate array is created and the
variable number now refers to this
“local” array.
at after the local array is created
3
3
arrayOne number
••• •••
This is a newly created array completely
separate and different from the argument
array. Changes made to this array will not
affect the original array.
badMethod(arrayOne);
public void badMethod(double[] number) {
...
number = new double[N];
...
}
4
Memory space for badMethod is deallocated
upon exiting the method. The array created
by this method now has no variable referring
to it, so this array is deallocated also.
at after badMethod
4
4
arrayOne
•••
Figure 10.12 Continuation of Figure 10.11.
wu23399_ch10.qxd 12/28/06 12:38 Page 575
10.5 Two-Dimensional Arrays
A table organized in rows and columns is a very effective means for communicating
many different types of information. Figure 10.13 shows sample data displayed in a
tabular format. In Java, we represent tables as two-dimensional arrays. The arrays
we have discussed so far are one-dimensional arrays because they have only one
index. In this section, we describe how two-dimensional arrays are used in Java.
Let’s begin with an example. Consider the following table with four rows and
five columns. The table contains the hourly rate of programmers based on their skill
level. The rows (horizontal) represent the grade levels, and the columns (vertical)
576 Chapter 10 Arrays and Collections
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
2
1 2
4
6
8
10
12
14
16
18
3
3
6
9
12
15
18
21
24
27
4
4
8
12
16
20
24
28
32
36
5
5
Multiplication Table
10
15
20
25
30
35
40
45
6
6
12
18
24
30
36
42
48
54
7
7
14
21
28
35
42
49
56
63
8
8
16
24
32
40
48
56
64
72
9
9
18
27
36
45
54
63
72
81
Los Angeles
San Francisco
San Jose
San Diego
Monterey
Los
Angeles
San
Francisco San Jose San Diego Monterey
—
600
500
150
450
600
—
100
750
150
500
100
—
650
50
150
750
650
—
600
450
150
50
600
—
Distance Table (in miles)
Tuition Table
Grades 1–6
Grades 7–8
Grades 9–12
$16,000.00
$19,000.00
$22,500.00
Day
Students
$28,000.00
$31,000.00
$34,500.00
Boarding
Students
Figure 10.13 Examples of information represented as tables.
two-
dimensional
array
wu23399_ch10.qxd 12/28/06 12:38 Page 576
represent the steps within a grade level. Reading the table, we know a programmer
with skill grade level 2, step 1 earns $36.50 per hour.
10.5 Two-Dimensional Arrays 577
Figure 10.14 Accessing an element of a two-dimensional array.
0
0
1
1
2
Row # Column #
2 36.50
3
3
4
payScaleTable[ 2 ][ 1 ]
Step
0 1 2 3 4
0 10.50 12.00 14.50 16.75 18.00
1 20.50 22.25 24.00 26.25 28.00
2 34.00 36.50 38.00 40.35 43.00
3 50.00 60.00 70.00 80.00 99.99
Grade
We declare the pay scale table as
double[][] payScaleTable;
or
double payScaleTable[][];
and create the array as
payScaleTable = new double[4][5];
The payScaleTable array is a two-dimensional array because two indices—
one for the row and another for the column—are used to refer to an array element.
For example, to refer to the element at the second column (column 1) of the third
row (row 2), we say
payScaleTable[2][1]
Figure 10.14 illustrates how the two indices are used to access an array element of
a two-dimensional array.
wu23399_ch10.qxd 12/28/06 12:38 Page 577
Let’s go over some examples to see how the elements of two-dimensional
arrays are manipulated. This code finds the average pay of the grade 2 programmers.
double average, sum = 0.0;
for (int j = 0; j  5; j++) {
sum += payScaleTable[2][j];
}
average = sum / 5;
The next example prints out the pay difference between the lowest and highest
steps for each grade level.
double difference;
for (int i = 0; i  4; i++) {
difference = payScaleTable[i][4] - payScaleTable[i][0];
System.out.println(Pay difference at Grade Level  +
i +  is  + difference);
}
This code adds $1.50 to every skill level.
for (int i = 0; i  4; i++) {
for (int j = 0; j  5; j++) {
payScaleTable[i][j] += 1.50;
}
}
In the previous examples, we used literal constants such as 5 and 4 to keep them
simple. For real programs, we need to write a loop that will work for two-dimensional
arrays of any size, not just with the one with four rows and five columns. We can use
the length field of an array to write such a loop. Using the length field, we can
rewrite the third example as
for (int i = 0; i  payScaleTable.length; i++) {
for (int j = 0; j  payScaleTable[i].length; j++) {
payScaleTable[i][j] += 1.50;
}
}
Do you notice a subtle difference in the code? Let’s examine the difference
between the expressions
payScaleTable.length
and
payScaleTable[i].length
578 Chapter 10 Arrays and Collections
wu23399_ch10.qxd 12/28/06 12:38 Page 578
First, there is actually no explicit structure called two-dimensional array in Java.
We only have one-dimensional arrays in Java. However, we can have an array of
arrays, and this is how the conceptual two-dimensional array is implemented in
Java. The sample array creation
payScaleTable = new double[4][5];
is really a shorthand for
payScaleTable = new double[4][ ];
payScaleTable[0] = new double[5];
payScaleTable[1] = new double[5];
payScaleTable[2] = new double[5];
payScaleTable[3] = new double[5];
which is equivalent to
payScaleTable = new double[4][ ];
for (int i = 0; i  4; i++) {
payScaleTable[i] = new double[5];
}
Figure 10.15 shows the effect of executing the five statements. The expression
payScaleTable.length
refers to the length of the payScaleTable array itself.
And the expression
payScaleTable[1].length
refers to the length of an array stored at row 1 of payScaleTable.
0 0 1 2 3 4
1
2
3
payScaleTable
payScaleTable[1].length == 5
payScaleTable
payScaleTable.length == 4
0
1
2
3
10.5 Two-Dimensional Arrays 579
wu23399_ch10.qxd 12/28/06 12:38 Page 579
580 Chapter 10 Arrays and Collections
Figure 10.15 Executing the statements on the left in sequence will create the array of arrays shown on
the right.
payScaleTable
payScaleTable[3] = new double[5];
0
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
1
2
3
5
payScaleTable
payScaleTable[2] = new double[5];
0
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
1
2
3
4
payScaleTable
payScaleTable[1] = new double[5];
0
0 1 2 3 4
0 1 2 3 4
1
2
3
3
payScaleTable
payScaleTable[0] = new double[5];
0
0 1 2 3 4
1
2
3
2
payScaleTable
payScaleTable = new double[4][ ];
0
1
2
3
1
Executing... Will result in...
wu23399_ch10.qxd 12/28/06 12:38 Page 580
We call an array that is part of another a subarray. The payScaleTable has four
subarrays of the same length. Since we allocate the subarrays individually, we can
create subarrays of different lengths. The following code creates a triangular array
whose subarray triangularArray[i] has length i.
triangularArray = new double[4][ ];
for (int i = 0; i  4; i++)
triangularArray[i] = new double[i+1];
The resulting triangularArray looks like this:
An array of arrays can be initialized at the time of declaration. The following
declaration initializes the payScaleTable array:
double[][] payScaleTable
= { {10.50, 12.00, 14.50, 16.75, 18.00},
{20.50, 22.25, 24.00, 26.25, 28.00},
{34.00, 36.50, 38.00, 40.35, 43.00},
{50.00, 60.00, 70.00, 80.00, 99.99} };
Here’s the complete sample program:
triangularArray
0
0
0 1
0 1 2
0 1 2 3
1
2
3
10.5 Two-Dimensional Arrays 581
/*
Chapter 10 Sample Program: Sample program for processing
2-D array of double.
File: Ch10PayScaleTable.java
*/
class Ch10PayScaleTable {
public static void main (String[] args) {
double[][] payScaleTable
= { {10.50, 12.00, 14.50, 16.75, 18.00},
{20.50, 22.25, 24.00, 26.25, 28.00},
{34.00, 36.50, 38.00, 40.35, 43.00},
{50.00, 60.00, 70.00, 80.00, 99.99} };
wu23399_ch10.qxd 12/28/06 12:38 Page 581
//Find the average pay of level 2 employees
double sum = 0.0, average;
for (int j = 0; j  5; j++) {
sum += payScaleTable[2][j];
}
average = sum / 5;
System.out.println( Average of Level 2 Employees:  + average );
System.out.println(n);
//Display the pay difference at each grade level
double difference;
for (int i = 0; i  4; i++) {
difference = payScaleTable[i][4] - payScaleTable[i][0];
System.out.println(Pay difference at Grade Level  +
i +  is  + difference);
}
//Print out the pay scale table
System.out.println(n);
for (int i = 0; i  payScaleTable.length; i++) {
for (int j = 0; j  payScaleTable[i].length; j++) {
System.out.print( payScaleTable[i][j] +   );
}
System.out.println();
}
//Increase the pay by 1.50 for every level/step
//and display the resulting table
System.out.println(n);
for (int i = 0; i  payScaleTable.length; i++) {
for (int j = 0; j  payScaleTable[i].length; j++) {
payScaleTable[i][j] += 1.50;
System.out.print(payScaleTable[i][j] +  );
}
System.out.println();
}
}
}
582 Chapter 10 Arrays and Collections
wu23399_ch10.qxd 12/28/06 12:38 Page 582
We can nest for-each loops to process a two-dimensional array. Remember
that the two-dimensional array is structurally an array of arrays (as illustrated in
Figure 10.15), and the nested for-each loops will make this fact explict. To print out
the pay scale table, for example, we write
for (double[] row : payScaleTable) {
for (double pay : row) {
System.out.print(pay +  );
}
System.out.println();
}
TheouterloopiteratesovertherowsinthepayScaleTable two-dimensionalarray.Each
row is one-dimensional array of double, so the type is declared as double[]. And the
inner loop iterates over the elements in each row. Notice that we cannot rewrite the
other loop statements in the Ch10PayScaleTable program by using the for-each loop.
There is no limit to the number of dimensions an array can have. We can
declare three-dimensional, four-dimensional, and higher-dimensional arrays. How-
ever, arrays with a dimension higher than 2 are not frequently used in object-
oriented languages. For example, data that were represented as a three-dimensional
array in a non-object-oriented language can be represented more naturally as a one-
dimensional array of objects with each object containing an array or some other
form of data structure (see Exercise 12 on page 615).
10.6 Lists and Maps 583
1. Write a code fragment to compute the average pay of the pays stored in the
payScaleTable array.
2. Write a code fragment that finds the largest integer in this two-dimensional array.
int[][] table = new int[10][10];
3. What is an output from this code?
int[][] table = new int[10][5];
System.out.println(table.length);
System.out.println(table[4].length);
10.6 Lists and Maps
Once an array is created, its capacity cannot be changed. For example, if we create
an array of 20 elements, then we are limited to store at most 20 elements in using
this array. If we need to add elements, then we have to create a new array. (Note: We
will learn how to do this in Section 10.7.) We call the condition in which an array
does not have any unused position left to add another element an array overflow.
array overflow
wu23399_ch10.qxd 12/28/06 12:38 Page 583
Bad Version
Whenever we use arrays in an application, we need to consider the possibility
of an array overflow. We can usually avoid an array overflow by declaring its capac-
ity to be a large number. However, if we declare the capacity of an array too large,
we may avoid an array overflow but end up underutilizing the space (e.g., using only
20 positions in an array with the capacity of 500). If we do not want our application
to be limited to some fixed capacity, we need to write code that handles an array
overflow by allocating a larger array.
If we need to handle an array overflow for multiple arrays we use in an appli-
cation, we can define a class that handles the array overflow so we don’t have to im-
plement an overflow-handling code for individual arrays. We might call the new
class ExpandableArray. By using this class, we can keep adding new elements with-
out worrying about the overflow condition, because the class handles the overflow
condition automatically.
Itturnsoutthere’snoneedforustowritesuchanExpandableArray classbecause
the Java standard library java.util already includes various classes and (interfaces) for
maintaining a collection of objects. They are collectively referred as the Java Collec-
tion Framework, or JCF. We will study the basic ones in this section.
The first is the List interface. Like a class, an interface is a reference data type;
but unlike a class, an interface includes only constants and abstract methods. An
abstract method has only the method header (or, more formally, the method proto-
type); that is, it has no method body. The abstract methods of an interface define a
behavior. For example, the List interface includes 25 abstract methods that collec-
tively define a behavior of a linear list, such as adding an element, removing an
element, and so forth.
We cannot create an instance of a Java interface. (Note: To differentiate a user
interface from a reference data type interface, we will use the term Java interface to
refer to the latter.) For example, the following will result in a compile-time error:
List myList = new List ( );
To create an instance that will support a List behavior, we need a class that im-
plements the List interface. We say a class implements an interface if it provides the
method body to all the abstract methods defined in the Java interface.
There are two classes in JCF that implement the List interface: ArrayList and
LinkedList. Because they implement the same interface, they behave exactly the
same. That is, there’s no difference in using them (as long as we use the methods
defined in the List interface). They differ in the internal data structure they use to
implement the interface. The ArrayList class uses an array, and the LinkedList class
uses a technique called linked-node representation. We choose one over the other
depending on the nature of application (e.g., choose LinkedList if the application
requires frequent insertions and deletions of elements but occasional searching for
elements in the list). It is beyond our scope to provide an in-depth comparative
analysis here. In most situations, the ArrayList class would be preferable so we will
use it for the examples in this chapter. We will provide more detailed coverage and
analysis on the array versus linked-node representation in Chapters 16 and 18.
584 Chapter 10 Arrays and Collections
JCF
interface
abstract
method
java.util
wu23399_ch10.qxd 12/28/06 12:38 Page 584
Let’s study how we can use the methods of the List interface. First we need to
declare and create an instance of a class that implements the List interface. Let’s use
the ArrayList class. From what we have learned so far about declaring and creating
an instance of a class, we would write something like
ArrayList myList;
... Not recommended
myList = new ArrayList();
if the class is ArrayList. This would work (you’ll get only a compiler warning), but it
is not a recommended style of programming. There are two improvements we
should make. The first is to declare the variable as the Java interface and to assign an
instance of the class that implements the interface. Applying this improvement will
result in
List myList; myList is declared as
... type List
myList = new ArrayList();
Basically this style of declaration improves the ease of program modification.
Suppose, for example, there are many methods that accept a list object
public void myMethod(ArrayList aList)
With this declaration, we can only pass an instance of ArrayList. Now suppose at a
later time we decide to use LinkedList to improve performances for certain types of
operations. We have to go back and make changes to all those method headers. But
what if we declare the method from the beginning as follows?
public void myMethod(List aList)
Since we can pass an instance of either ArrayList or LinkedList (or an instance of any
class that implements the List interface), no changes are required. We will study the
Java interface in greater detail in Chapter 13.
The second improvement is specific to Java 5.0 JCF classes. If we declare a
list object as
List myList = new ArrayList();
there are no restrictions on the type of objects we can add to the list. For example, we
can add String objects, Person objects, Vehicle objects, and so forth to this myList.
10.6 Lists and Maps 585
There is another “expandable array” in the JCF called Vector. The Vector class pre-
dates the JCF classes,but from Java 2 SDK 1.2,the class was modified to implement
the List interface. Because it was designed before the JCF classes, it includes many
methods in addition to the List methods. In general, the ArrayList class is recom-
mended for most situations.
wu23399_ch10.qxd 12/28/06 12:38 Page 585
We call such list a heterogeneous list. In most applications, there is no need to
maintain such heterogeneous lists. What we need is a homogeneous list, where the
elements are restricted to a specific type such as a list of Person objects, a list of
String objects, a list of Book objects, and so forth. Specifying the element type im-
proves the program reliability because an error such as trying to add a wrong type of
object to a list can be caught during the compile time. It is strongly recommended to
use homogeneous lists.
To specify a homogeneous list, we must include the type of elements in the
declaration and creation statements. Here’s an example that declares and creates a
list of Person objects:
ListPerson friends;
...
friends = new ArrayListPerson( );
The general syntax for the declaration is
interface-or-class-name  element-type  identifier;
And the general syntax for the creation is
identifier = new class-name  element-type  ( parameters ) ;
We can combine the two into a single statement as
interface-or-class-name  element-type  identifier
= new class-name element-type ( parameters ) ;
for example,
ListPerson friends = new ArrayListPerson{ );
Now we are ready to study the basic operations of the List interface. Once a list
is created properly, we can start adding elements. In the following example, we cre-
ate a list named friends and add four Person objects to the list:
ListPerson friends = new ArrayListPerson{ );
Person person;
person = new Person(Jane, 10, 'F');
friends.add(person);
person = new Person(Jack, 16, 'M');
friends.add(person);
person = new Person(Jill, 8, 'F');
friends.add(person);
person = new Person(John, 12, 'M');
friends.add(person);
586 Chapter 10 Arrays and Collections
add
heterogeneous
list
homogeneous
list
wu23399_ch10.qxd 12/28/06 12:38 Page 586
To find out the number of elements in a list, we use its size method. The
following code will print out 3:
ListString sample = new ArrayListString{ );
sample.add(One Java);
sample.add(One Java);
sample.add(One Java);
System.out.println(sample.size());
We can access objects in a list by giving their index position in the list, much
as we did with the array. We use the get method to access an object at index posi-
tion i. For example, to access the Person object at position 3 (note: the first element
is at position 0) in the friends list, we write
Person p = friends.get(3);
An invalid argument, such as a negative value or a value greater than size() – 1, will
result in an IndexOutOfBoundsException error.
One of the most common operations we perform on a list is traversal. This op-
eration, also called scanning or iteration, accesses all elements in a list. To traverse
a list from the first to the last element, we can use the for-each loop. Here’s how we
can print out the names of all those in the friends list by using the for-each loop:
for (Person p : friends) {
System.out.println(p.getName());
}
This for-each loop, new to Java 5.0, is actually a shortcut for using an iterator
pattern. When we call the iterator method of a list, it returns an Iterator object (an
instance of a class that implements the Iterator interface) that supports the two
methods hasNext and next. Here’s the code to print out the names of all those in the
friends list by using an iterator:
Person p;
IteratorPerson itr = friends.iterator();
while (itr.hasNext()) {
p = itr.next();
System.out.println(p.getName());
}
Again, the for-each loop is built on top of the iterator pattern as a syntactical
shortcut, and wherever the iterator pattern is available, we can (and should) use the
cleaner and less error-prone for-each loop.
10.6 Lists and Maps 587
get
size
traversal
iterator
wu23399_ch10.qxd 12/28/06 12:38 Page 587
The traversal operation is necessary to search a list for elements that meet
some criterion. Let’s say we want to print out the names of those in the friends list
who are older than 10. Here’s the code:
for (Person p : friends) {
if (p.age()  10) {
System.out.println(p.getName());
}
}
Instead of simply printing out their names, we could create another list to keep track
of those who are older than 10:
ListPerson olderThan10List = new ArrayListPerson();
for (Person p : friends) {
if (p.age()  10) {
olderThan10List.add(p);
}
}
The original friends list remains unchanged; that is, no objects are removed from
the list. We simply have a second list that points to a subset of elements in the
friends list. The situation can be illustrated as follows (two Person objects are older
than 10):
To remove an element from a list, we use the remove method. There are two
versions: one specifies the index position of the object to remove, and the other spec-
ifies the object itself (i.e., the reference to this object). If we use the first version,
here’s how we remove the Person object at index position 2 in the friends list:
friends.remove(2);
The second version of the remove method requires a reference to an object.
One way to acquire a reference to an object we want to remove is via traversal.
friends
olderThan10List
:Person :Person :Person :Person :Person :Person
•••
•••
588 Chapter 10 Arrays and Collections
remove
wu23399_ch10.qxd 12/28/06 12:38 Page 588
Bad Version
Here’s the code that traverses the friends list and removes all Person objects who are
older than 10:
ListPerson tempList = new ArrayListPerson();
//first we collect those we want to remove from the
//friends list in a separate list
for (Person p : friends) {
if (p.age()  10) {
tempList.add(p);
}
}
//then we remove every element in tempList
//from the friends list
for (Person p : tempList) {
friends.add(p);
}
Some of you might have thought about the following code:
for (Person p : friends) {
if (p.age()  10) {
friends.remove(p);
}
}
This is an invalid operation. We are not allowed to modify the list we are traversing.
The for-each loop (and the underlying iterator pattern) is a read-only traversal.
10.6 Lists and Maps 589
No changes can be made to a list while traversing it with an iterator or a for-each
loop.
Lists and Primitive Data Types
With an array, we can store either primitive data values (int, double, etc.) or ob-
jects. With a list, we can store only objects. If we need to store primitive data values
wu23399_ch10.qxd 12/28/06 12:38 Page 589
in a list, then we must use wrapper classes such as Integer, Float, and Double. To
add integers to a list, we have to do something like this:
ListInteger intList = new ArrayListInteger( );
intList.add(new Integer(15));
intList.add(new Integer(30));
...
When we access the elements of intList, which are Integer objects, we need to
use the intValue method to get the integer value. The following code computes the
sum of integer values stored in intList:
int sum = 0;
for (Integer intObj : intList) {
sum = sum + intObj.intValue();
}
Instead of this tedious way of dealing with primitive data values, Java 5.0 in-
troduces automatic boxing and unboxing features. With Java 5.0, we can write the
code as if we could store primitive data values in lists. For example, the following
code is valid with Java 5.0:
ListInteger intList = new ArrayListInteger( );
intList.add(15);
intList.add(30);
...
int sum = 0;
for (int value : intList) {
sum = sum + value;
}
Keep in mind that there are no structural changes. We are still adding Integer
objects to intList (see the declaration for intList). It is just a syntactical shortcut.
When we write, for example,
intList.add(30);
the compiler translates it to
intList.add(new Integer(30));
This is called auto boxing. And when we write
int num = intList.get(1);
590 Chapter 10 Arrays and Collections
wu23399_ch10.qxd 12/28/06 12:38 Page 590
the compiler translates it to
int num = intList.get(1).intValue( );
This is called auto unboxing.
Let’s conclude our discussion of the List interface with the BookTracker helper
class we used in the Sample Development section of Chapter 7. The BookTracker
class uses an ArrayList to keep track of library books. Here’s the definition:
10.6 Lists and Maps 591
/*
Chapter 7 Sample Development: Library Overdue Checker
File: BookTracker.java
*/
import java.util.*;
class BookTracker {
public static final int ERROR = -1;
private ListLibraryBook books;
public BookTracker( ) {
books = new LinkedListLibraryBook();
}
public void add(LibraryBook book) {
books.add(book);
}
public double getCharge( ) {
return getCharge(new GregorianCalendar()); //set today as due date
}
public double getCharge(GregorianCalendar returnDate) {
if (books.isEmpty()) {
return ERROR;
} else {
return totalCharge(returnDate);
}
}
public String getList( ) {
StringBuffer result = new StringBuffer();
String lineSeparator = System.getProperty(line.separator);
for (LibraryBook book: books) {
result.append(book.toString() + lineSeparator);
}
Constructor
add
getCharge
getList
wu23399_ch10.qxd 12/28/06 12:38 Page 591
return result.toString();
}
private double totalCharge(GregorianCalendar returnDate) {
double totalCharge = 0.0;
for (LibraryBook book: books) {
totalCharge += book.computeCharge(returnDate);
}
return totalCharge;
}
}
592 Chapter 10 Arrays and Collections
totalCharge
Map
Let’s move on to another useful interface called Map. There are two classes that im-
plement this interface: HashMap and TreeMap. We will describe the TreeMap class
in this section because this is the class we used in implementing the helper WordList
class in Chapter 9. The TreeMap class actually implements a subinterface of Map
called SortedMap, where the entries in the map are sorted.
A map consists of entries, with each entry divided into two parts: key and
value. No duplicate keys are allowed in the map. Both key and value can be an in-
stance of any class. The main advantage of a map is its performance in locating an
entry, given the key. Consider, for example, that we want to maintain a table of
course evaluations. For each course offered on campus, we want to keep an evalua-
tion that is summarized from the student opinion poll collected at the end of the
term. The course number (e.g., CS0101) is the key, and the evaluation is the value.
We would want to store the information as a map because we need to look up the
evaluation of a course efficiently, as there are hundreds or thousands of courses. The
search would take too long if we used other data structures.
As we know, a Java array allows only integer indices. In some situations we may
want to use an array with indices other than integers. For example, a WordList
from Chapter 9 can be viewed as an array of numbers with words (String) as its
indices. A map can be characterized as an expandable array with instances of
any class as its indices. So whenever we need an array of values with noninteger
indices, a map is a possible solution.
When declaring and creating a map, we must specify the type for the key and
the value. For example, to declare and create a map with String as both its key and
value, we write
MapString,String table;
table = new TreeMapString,String( );
wu23399_ch10.qxd 12/28/06 12:38 Page 592
We use its put method to add the key-value pairs to the map as
table.put(CS0101, Great course. Take it);
where the first argument is the key and the second argument is the value. To remove
an entry, we use the remove method with the key of an entry to remove from the
map, for example,
table.remove(CS2300);
Instead of removing individual elements, we can remove all of them at once
by calling the clear method. The statement
table.clear( );
removes everything from the map, making it an empty map.
To retrieve the value associated to a key, we call the map’s get method.
String courseEval = table.get(CS102);
We can ask the map if it contains a given key. To check, for example, whether
the map contains an evaluation for course number CS0455, we write
boolean result = table.containsKey(CS0455);
If there’s no matching entry, then the value null is returned.
To traverse a map, we must first call its entrySet method to get a set of ele-
ments. The method returns an instance of a class that implements the Set interface,
another interface in JCF, that models a mathematical set. Those interested in using
the Set interface are referred to the Java API documentation. The methods defined
in the Set interface are very similar to those defined in the List interface. If we
know how to use the List interface, then it won’t take long for us to understand the
Set interface.
An element in a map is a key-value pair, so the entrySet method returns a set
of key-value pairs. A key-value pair is an instance of a class that implements the
Map.Entry interface. The dot notation indicates that the Entry interface is defined in
the declaration of the Map interface. Such a nested declaration is useful in avoiding
a naming conflict.
Two useful methods defined in the Map.Entry interface are the getKey and
getValue, whose purpose is to retrieve the key and the value of an entry, respec-
tively. To put it all together, here’s an example that outputs the course numbers and
their evaluations stored in the table map:
for (Map.EntryString,String entry : table.entrySet()) {
System.out.println(entry.getKey() + :n +
entry.getValue() + n);
}
10.6 Lists and Maps 593
put
remove
clear
get
contains key
entrySet
wu23399_ch10.qxd 12/28/06 12:38 Page 593
Notice the type declaration for the loop variable entry is Map.EntryString, String.
Because the key and value component of Map.Entry can be of any class, we need to
indicate the actual type for the key and the value specific to this entry.
We are now ready to present the WordList class. It uses a TreeMap object to
keep track of distinct words in a document and how many times they occur in the
document. Notice the TreeMap class actually implements a more specialized map
interface called SortedMap, a subinterface of the Map interface that adds the be-
havior of sorting the elements in ascending key order. This is exactly the data struc-
ture we want to use here because we want to access and display the words and their
count in alphabetical order. Here’s the definition:
594 Chapter 10 Arrays and Collections
/*
Chapter 9 Sample Development: Word Concordance
File: WordList.java
*/
import java.util.*;
class WordList {
SortedMapString, Integer table;
public WordList( ) {
table = new TreeMapString, Integer();
}
public void add(String word) {
int val;
if (table.containsKey(word)) {
val = table.get(word) + 1;
} else {
//word occurs for the first time
val = 1;
}
table.put(word, val);
}
public String getConcordance( ){
String line;
String lineTerminator
= System.getProperties().getProperty(line.separator);
StringBuffer strBuf = new StringBuffer();
for (Map.EntryString,Integer entry : table.entrySet()) {
Constructor
add
getConcordance
Auto boxing and unboxing
are used in this method.
wu23399_ch10.qxd 12/28/06 12:38 Page 594
line = entry.getValue().toString() + t +
entry.getKey() + lineTerminator;
strBuf.append(line);
}
return strBuf.toString();
}
public void reset( ) {
table.clear();
}
}
10.6 Lists and Maps 595
Compared to the amount of work the class has to perform, the length of its
source code is rather short. This is so because the hard part of maintaining the data
structure is done by the TreeMap class. Had we tried to implement the WordList
class without using the TreeMap class, the source code would have been much
longer.Alittle effort to study the JCF classes pays handsomely when the time comes
for us to implement an efficient data manager class, such as the WordList class.
reset
1. What is the output from the following code?
ListString list = new ArrayListString();
for(int i = 0; i  6; i++) {
list.add(element  + i);
System.out.println(list.size());
}
2. What is the output from the following code?
ListString list = new ArrayListString();
for(int i = 0; i  6; i++) {
list.add(element  + i);
}
list.remove(1);
list.remove(3);
System.out.println(list.get(2));
3. Identify all errors in the following code.
ListString list = new ArrayListInteger();
ListPerson people = new ListPerson();
MapString table = new Map();
wu23399_ch10.qxd 12/28/06 12:38 Page 595
596 Chapter 10 Arrays and Collections
The Address Book
In this section, we will design a class called an AddressBook to maintain a collection of
Person objects. The AddressBook class is implemented by using an array. We will use
the Person class defined in Section 10.2. Through the design of the AddressBook
class, we will reiterate the key principles of object-oriented design.
Notice that we are not developing a complete program here. We are designing
only one of the many classes we need for a complete address book program. For the
complete program, we need a main window, objects for doing input and output, and so
forth. In this section, we will concentrate on one class that is only responsible for main-
taining a collection of Person objects.This class will not perform, for example, input and
output of Person objects, following the single-task object (STO) principle introduced in
Chapter 4. We will discuss the importance of the STO principle while we develop the
AddressBook class. One objective we have in designing the AddressBook class is to
make the class reusable in many different programs. Many of the design decisions we
will make during the development are based on implementing a reusable class.
Problem Statement
Write an AddressBook class that manages a collection of Person objects. An
AddressBook object will allow the programmer to add, delete, or search for a
Person object in the address book.
Overall Plan
Our first task is to come up with an overall design of the class.Let’s begin by first identify-
ing the core operations that an address book object must support. The problem state-
ment indicated three major operations: add, delete, and search. These three operations
are pretty much a standard in any collection of data values.For any kind of collections,you
will always want to be able to add a new item, delete an old item, and search for an item
or items.An address book is no exception as it is a collection of information about people
for whom you would want to add,delete,and search data.
Our task here is to design a class that will maintain an address book by supporting
thesethreeoperations.Wewilldefinethreemethodsfortheclass: add,delete,andsearch.
Our working design document for the AddressBook class is therefore as follows:
Sample Development
10.7 Sample Development
Design Document:The Public Methods of the AddressBook Class
Method Purpose
AddressBook A constructor to initialize the object. We will include multiple
constructors as necessary.
add Adds a new Person object to the address book.
delete Deletes a specified Person object from the address book.
search Searches for a specified Person object in the address book
and returns this person if found.
wu23399_ch10.qxd 12/28/06 12:38 Page 596
10.7 Sample Development 597
We will implement the class in this order:
1. Implement the constructor(s).
2. Implement the add method.
3. Implement the search method.
4. Implement the delete method.
5. Finalize the class.
This order of development follows a natural sequence. To implement any instance
method of a class, we need to be able to create a properly initialized object, so we will
begin the class implementation by defining a constructor. As a part of defining a con-
structor,we will identify necessary data members.We will add more data members as we
progress through the development steps.The second step is to implement the add rou-
tine, because without being able to add a new Person object, we won’t be able to test
other operations. For the third step, we will implement the search routine. And for the
fourth step,we will implement the last routine.Although we could implement the delete
routine before the search routine,we need some form of searching to test the correctness
of the delete routine. In other words, we delete a person and attempt to search for this
person, verifying that the search will not find the deleted person. So we will implement
the search routine before the delete routine.
Step 1 Development: Skeleton with Constructors
In step 1, we will identify the data members and define the constructor(s) to initialize
them.The key data member for the class is a structure we will use to keep track of a col-
lection of Person objects.We will use an array for this data structure. Our decision to use
an array is based on pedagogy.Using a List from JCF will simplify our development effort,
but it is more important to learn how to use arrays.
We will create an array of Person objects in the constructor. At the time we create
an array, we must declare its size. Remember that the size of an array is the maximum
number of elements this array can hold.The actual number of Person objects stored in
the array will be anywhere from zero to the size of the array.
We have two possible alternatives for specifying the size of an array. First, we can
let the programmer pass the size as an argument to the constructor. Second, we can
set the size to a default value. Both alternatives are useful. If the programmer has a
good estimate of the number of Person objects to manage, she can specify the size in
the constructor. Otherwise, she can use the default size by not specifying the size in
the constructor. We will define two constructors to support both alternatives. This will
give programmers flexibility in creating an AddressBook object.
If we are going to provide a constructor in which the programmer can pass the
size of an array, then we need to write the constructor so it won’t crash when an invalid
value is passed as an argument.What would be an invalid argument value? Since we are
dealing with a collection of objects and the size of a collection cannot be negative, an
argument value of less than zero is invalid. Also, even though a collection whose size is
zero may make sense in theory, such a collection makes no sense in practice.Therefore,
develop-
ment steps
step 1
design
wu23399_ch10.qxd 12/28/06 12:38 Page 597
10.7 Sample Development—continued
we will consider zero also as an invalid argument value.We will require an argument to
a constructor to be a positive integer. We will throw an IllegalArgumentException for
an invalid value.
At this point, we have only one data member—an array of objects. We will call it
entry because a Person object is a single entry in an address book. We will set the
default size of entry to 25. There is no particular reason for selecting this size. We
simply picked a number that is not too small or too big. We can change this value later
if we need to.
We will define two constructors. The first constructor will call the second con-
structor with the value 25 (default size) as its argument.The second constructor creates
an array of Person objects of the size passed as its parameter. Inside the second con-
structor, we include a temporary test output statement.The class is defined as follows:
598 Chapter 10 Arrays and Collections
step 1 code
/**
* This class is designed to manage an address book that contains
* Person objects. The user can specify the size of the address book
* when it is created. If no size is specified, then the default size
* is set to 25 Person objects.
*
* @author Dr. Caffeine
*
*/
class AddressBook {
private static final int DEFAULT_SIZE = 25;
private Person[] entry;
public AddressBook( ) {
this( DEFAULT_SIZE );
}
public AddressBook(int size) {
if (size = 0 ) { //invalid data value, use default
throw new IllegalArgumentException(Size must be positive.);
}
entry = new Person[size];
System.out.println(array of + size +  is created.); //TEMP
}
}
Constructors
Data members
wu23399_ch10.qxd 12/28/06 12:38 Page 598
To test this class,we have included a temporary output statement inside the second
constructor. We will write a test program to verify that we can create an AddressBook
object correctly.The test data are as follows:
10.7 Sample Development 599
step 1 test
Step 1 Test Data
Data Value Purpose
Negative numbers Test the invalid data.
0 Test the end case of invalid data.
1 Test the end case of valid data.
 1 Test the normal cases.
We will use a very simple test program:
/*
Chapter 10 Sample Program: A test main program for
verifying the step 1 AddressBook class.
File: TestAddressBook.java
*/
import java.util.*;
class TestAddressBook { //Step 1 Test Main
public static void main(String args[]) {
AddressBook myBook;
String inputStr;
int size;
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print(Array size: );
inputStr = scanner.next();
if (inputStr.equalsIgnoreCase(stop)) {
break;
}
size = Integer.parseInt(inputStr);
wu23399_ch10.qxd 12/28/06 12:38 Page 599
10.7 Sample Development—continued
600 Chapter 10 Arrays and Collections
try {
myBook = new AddressBook(size);
} catch (IllegalArgumentException e) {
System.out.println(Exception Thrown: size =  + size);
}
}
}
}
Run the program several times with a different set of test data and verify that we
get the correct results.
Step 2 Development: Implement the add Method
In the second development step, we will implement the add method.We mentioned in
the overall design step that this class will not do any input or output of person data.
This decision is based on the STO principle. A single object doing both the input/
output routines and maintaining the array will reduce its usability. For example, had the
AddressBook class used some GUI objects to handle the input and output of person
data, the use of this class would dictate or impose the style of input and output rou-
tines on the programmers. The programmer will not have an option of using the input
and output objects appropriate for his or her uses.
Following the STO principle, we will let the programmer decide how she will
input and output person data. The task of the add method is to accept a Person ob-
ject as its parameter and add the passed Person object to the array. Since the array is
limited in size, what should we do if there is no more space to add another Person
object? There are two alternatives. Alternative design 1 is to return false if a new
Person object cannot be added to the array, that is, if the array is full. The method will
return true otherwise. Alternative design 2 is to increase the array size. Since the size
of an array object cannot be changed once the object is created, we need to create
another array with a larger size than that of the original if we choose to implement
the second alternative.
Since the second alternative is more accommodating and less restrictive to the
programmer, we will implement this alternative. When the array is full, we will create a
new array, copy the objects from the original array to this new array, and finally set the
variable entry to point to this new array.We will set the size of the new array to 1.5 times
larger than the original array.This size increment is just an estimate. Any value between
125 and 200 percent of the old array is reasonable.You don’t want to make it too small,
step 2
design
alternative
design 1
alternative
design 2
wu23399_ch10.qxd 12/28/06 12:38 Page 600
say, 105 percent, since that will cause the enlarge method to be called too frequently.
You don’t want to make it too large either, since that will likely result in wasted space.
Figure 10.16 illustrates the process of creating a larger array.
Now let’s think about how to add a Person object to the array. To add a new ob-
ject, we need to locate a position where we can insert the object. Since we are not
maintaining Person objects in any particular order, we will add a new person at the
first available position. If we fill the positions from the low to high indices (0, 1, 2, . . .),
we can use a variable to remember the index of the next available position. Since we
10.7 Sample Development 601
A
0 1 2 3
entry
B C D
:Person :Person :Person :Person
0 1 2 3 4 5
temp
Person[] temp;
int newLength = (int) (1.5 * entry.length);
temp = new Person[newLength];
A
0 1 2 3
entry
B C D
:Person :Person :Person :Person
0 1 2 3 4 5
temp
for (int i = 0; i  entry.length; i++) {
temp[i] = entry[i];
}
entry = temp;
Note: The old array will eventually
get returned to the system via
garbage collection.
Figure 10.16 How a new array that is 150 percent of the original array is created. The size of the
original array is 4.
wu23399_ch10.qxd 12/28/06 12:38 Page 601
10.7 Sample Development—continued
602 Chapter 10 Arrays and Collections
are using an array, the index of the next available position is also the number of Person
objects currently in the array, so we will call this variable count. Figure 10.17 illustrates
the add operation.
First we add a new instance variable count to the class:
//--------------------------
// Data Members
//--------------------------
private int count; //number of elements in the array,
//which is also the position at which to add
//the next Person object
We modify the constructor to initialize this data member:
public AddressBook(int size) {
count = 0;
//same as before
}
(Note: Because we defined the first constructor to call the second constructor, we
can implement this change by rewriting only one constructor instead of two.) The add
0 1
count  2
newPerson
2 3
entry
0 1
count  3
Before After
2 3
entry
entry[count] = newPerson;
count++;
A
:Person
A
:Person
B
:Person
B
:Person
C
:Person
newPerson
C
:Person
Figure 10.17 Adding a new Person object to the next available location.The array length is 4.
step 2 code
wu23399_ch10.qxd 12/28/06 12:38 Page 602
10.7 Sample Development 603
method is defined as follows:
public void add(Person newPerson) {
assert count =0 
count = entry.length;
if (count == entry.length) { //no more space left,
enlarge( ); //create a new, larger array
}
//at this point, entry refers to a new, larger array
entry[count] = newPerson;
count++;
}
Notice the use of the assertion feature.We place the assert statement to make sure
the value of count is valid. The expand method is a new private method that creates a
new,larger array.
Design Document:The AddressBook Class
Method Visibility Purpose
. . . . . . . . .
expand private Creates a new array that is 150 percent of the
old array.
private void expand( ) {
//create a new array whose size is 150% of
//the current array
int newLength = (int) (1.5 * entry.length);
Person[] temp = new Person[newLength];
//now copy the data to the new array
for (int i = 0; i  entry.length; i++) {
temp[i] = entry[i];
}
//finally set the variable entry to point to the new array
entry = temp;
System.out.println(Inside the method enlarge); //TEMP
System.out.println(Size of a new array: 
+ entry.length); //TEMP
}
Notice the use of the
assertion feature here.
wu23399_ch10.qxd 12/28/06 12:38 Page 603
10.7 Sample Development—continued
We will write a test program to verify that a new Person object is added to the
array correctly. In addition, we need to test that a new array 150 percent larger than the
old one is created when there are no more spaces left in the array. The test data are as
follows:
604 Chapter 10 Arrays and Collections
step 2 test
The step 2 test program is as follows:
/*
Chapter 10 Sample Program: A test main program for
verifying the step 2 AddressBook class.
File: TestAddressBook.java
*/
class TestAddressBook {
public static void main(String[] args) {
AddressBook myBook;
Person person;
myBook = new AddressBook(4);
//add four Person objects
for (int i = 0; i  4; i++) {
person = new Person(Ms. X + i, 10, 'F');
myBook.add(person);
}
//add the fifth person and see if
//a new array is created
Step 2 Test Data
Test Sequence Purpose
Create the array of size 4. Test that the array is created correctly.
Add four Person objects. Test that the Person objects are added
correctly.
Add the fifth Person object. Test that the new array is created and the
Person object is added correctly (to the new
array).
wu23399_ch10.qxd 12/28/06 12:38 Page 604
person = new Person(fifth one, 10, 'F');
myBook.add(person);
}
}
10.7 Sample Development 605
step 3
design
Run the program several times with different sizes for the address book and verify
that we get the correct results.
Step 3 Development: Implement the search Method
In the third development step, we implement the search method.This method can return
one or more Person objects that meet the search criteria.We have several options for the
search criteria.Since we keep track of name,age,and gender for each person,we can use any
one of these values as the search criterion. In this implementation, we will use the person’s
name.The search routine for the other two criteria will be left as an exercise (see Exercise 14).
To implement the search method, we will make an assumption that the name is
unique so that there will be at most one matching Person object. If the name is not
unique, then there are two possibilities.The search method can return one Person ob-
ject (among many) that matches the given name or return all Person objects that
match the given name.We will leave the case when the name is not unique as an exer-
cise (see Exercise 13). Notice that the add method we implemented in step 2 does not
check the person data. In other words, there is no mechanism to disallow the addition
of a Person object with a duplicate name. We will leave the implementation of the
modified add method as an exercise (see Exercise 15).
There are two possible outcomes with the search method—a successful or an un-
successful search.The method has to return a value by which the programmer can verify
the result of the search.We will define the search method so that it will return a matching
Person object if it is found and will return null otherwise. The search routine will start
scanning the array from the first position until the desired Person object is found (suc-
cessful search) or until no more Person objects are left in the array (unsuccessful search).
Expressing the search routine in pseudocode,we have
loc = 0;
while (loc  count 
name of Person at entry[loc] != searchName) {
loc++;
}
if (loc == count) {
foundPerson = null;
} else {
foundPerson = entry[loc];
}
return foundPerson;
wu23399_ch10.qxd 12/28/06 12:38 Page 605
10.7 Sample Development—continued
Translating the pseudocode to an actual method will result in the following method:
public Person search(String searchName) {
Person foundPerson;
int loc = 0;
assert count = 0  count = entry.length;
while (loc  count 
!searchName.equals(entry[loc].getName())) {
loc++;
}
if (loc == count) {
foundPerson = null;
} else {
foundPerson = entry[loc];
}
return foundPerson;
}
To test the search method, we will build an address book that contains five
Person objects. We will give names Ms.X0, Ms.X1, . . . , and Ms.X4 to them. After the
address book is set up, we test various cases of the search. We test for successful and
unsuccessful searches. For the successful searches, we test for the end cases and nor-
mal cases. The end cases involve searching for persons stored in the first and last posi-
tions of the array. Off-by-1 error (OBOE) is very common in processing an array, so it is
very important to test these end cases.
After a successful execution, we will test the class again by changing the size of
the array.One test size we should not forget to test is the end case for the array size,which
is 1.Also,we need to test the cases where the array is not fully filled,such as an array of size
5 containing only two Person objects.
The test data are as follows:
606 Chapter 10 Arrays and Collections
step 3 code
step 3 test
Step 3 Test Data
Test Sequence Purpose
Create the array of size 5 and add
five Person objects with unique
names.
Test that the array is created and set up
correctly.Here,we will test the case where
the array is 100 percent filled.
Search for the person in the first
position of the array.
Test that the successful search works
correctly for the end case.
wu23399_ch10.qxd 12/28/06 12:38 Page 606
The step 3 test program is written as follows:
10.7 Sample Development 607
Search for the person in the last
position of the array.
Search for a person somewhere
in the middle of the array.
Test the normal case.
Test another version of the end case.
Search for a person not in the array. Test for the unsuccessful search.
Repeat the above steps with an array
of varying sizes,especially the array
of size 1.
Test that the routine works correctly for
arrays of different sizes.
Repeat the testing with the cases where
the array is not fully filled,say,array
length is 5 and the number of objects
in the array is 0 or 3.
Test that the routine works correctly for
other cases.
/*
Chapter 10 Sample Program: A test main program for
verifying the step 3 AddressBook class.
File: TestAddressBook.java
*/
class TestAddressBook {
AddressBook myBook;
Person person;
public static void main(String[] args) {
TestAddressBook tester = new TestAddressBook();
tester.setupArray(5);
tester.testSearch();
}
public void setupArray(int N) {
myBook = new AddressBook( N );
//add N Person objects
for (int i = 0; i  N; i++) {
person = new Person(Ms. X+i, 10, 'F');
myBook.add(person);
}
}
public void testSearch( ) {
//test for the end case
person = myBook.search(Ms. X2);
wu23399_ch10.qxd 12/28/06 12:38 Page 607
10.7 Sample Development—continued
if (person == null) {
System.out.println
(Error: Didn't find the person it should);
} else {
System.out.println
(person.getName() +  is found okay.);
}
}
}
608 Chapter 10 Arrays and Collections
Notice the TestAddressBook class is now an instantiable main class.Since the code
for testing is getting longer, it is not practical anymore to do everything in a single main
method. For testing, we will modify the method body of setupArray and testSearch as
necessary to test all other cases described in the test data table.
Step 4 Development: Implement the delete Method
In the fourth development step, we implement the delete method. To delete a Person
object,the programmer must somehow specify which Person object to remove from the
address book. As we did with the search method, we will use the name of a person to
specify which person to delete.Since we assume the name is unique,the delete method
will remove at most one Person object. There are two possible outcomes: the specified
person is removed from the address book (successful operation) and the specified person
is not removed because he or she is not in the address book (unsuccessful operation).We
will define the delete method so that it will return true if the operation is successful and
false otherwise.
The removal of an element in an array of objects is done by setting the element to
null. This will leave a“hole.”We will fill this hole by replacing the removed element with
the last element, as explained earlier (see Figure 10.8).This filling operation is necessary
for other methods,specifically the add method,to work correctly.
To fill the hole, we need to know the location of the hole.To find this location, we
write a private search method called findIndex. The method is very similar to the search
method.The only difference is that the return value of findIndex is an index of an element
in the array,whereas the return value of search is a Person object.By using this findIndex
method,the delete method can be expressed as
boolean status;
int loc;
loc = findIndex( searchName );
step 4
design
wu23399_ch10.qxd 12/28/06 12:39 Page 608
if (loc is not valid) {
status = false;
} else { //found, pack the hole
replace the element at index loc+1 by the last element
at index count;
status = true;
count--; //decrement count,
//since we now have one less element
assert count is valid;
}
return status;
The private findIndex method will look like this:
private int findIndex(String searchName) {
int loc = 0;
assert count =0  count = entry.length;
while (loc  count 
!searchName.equals(entry[loc].getName())) {
loc++;
}
if (loc == count) {
loc = NOT_FOUND;
}
return loc;
}
The constant NOT_FOUND is set in the data member section as
//---------------------------
// Data Members
//---------------------------
private static final int NOT_FOUND = -1;
By using this findIndex method,the delete method is defined as follows:
public boolean delete(String searchName) {
boolean status;
int loc;
loc = findIndex(searchName);
if (loc == NOT_FOUND) {
status = false;
} else { //found, pack the hole
10.7 Sample Development 609
step 4 code
wu23399_ch10.qxd 12/28/06 12:39 Page 609
10.7 Sample Development—continued
entry[loc] = entry[count-1];
status = true;
count--; //decrement count,
//since we now have one less element
assert count = 0  count = entry.length;
}
return status;
}
To test the delete method,we will build an address book that contains five Person
objects, as before.Test cases are to delete the first person in the array, delete the last per-
son in the array,delete someone in the middle (normal case),and try to delete a nonexis-
tent person.
After a successful execution, we will test the class again by changing the size of an
array.One test size we should not forget to test is the end case for the array size, which is
1.Also,we need to test the cases where the array is not fully filled,such as an array of size
5 containing only two Person objects.
The test data are as follows:
610 Chapter 10 Arrays and Collections
step 4 test
Step 4 Test Data
Test Sequence Purpose
Create the array of size 5 and add five
Person objects with unique names.
Search for a person to be deleted next. Verify that the person is in the array
before deletion.
Delete the person in the array. Test that the delete method works
correctly.
Search for the deleted person. Test that the delete method works
correctly by checking that the value
null is returned by the search.
Attempt to delete a nonexistent person. Test that the unsuccessful operation
works correctly.
Repeat the above steps by deleting
persons at the first and last positions.
Test that the routine works correctly for
arrays of different sizes.
Repeat testing where the array is not fully
filled,say,an array length is 5 and the
number of objects in the array is 0 or 3.
Test that the routine works correctly for
other cases.
Test that the array is created and set up
correctly.Here,we will test the case where
the array is 100 percent filled.
wu23399_ch10.qxd 12/28/06 12:39 Page 610
The step 4 test program is written as follows:
10.7 Sample Development 611
/*
Chapter 10 Sample Program: A test main program for
verifying the step 4 AddressBook class.
File: TestAddressBook.java
*/
class TestAddressBook {
AddressBook myBook;
Person person;
public static void main(String[] args) {
TestAddressBook tester = new TestAddressBook();
tester.setupArray( 5 );
tester.testDelete( );
}
public void setupArray(int N) {
myBook = new AddressBook( N );
//add N Person objects
for (int i = 0; i  N; i++) {
person = new Person( Ms. X + i, 10, 'F' );
myBook.add(person);
}
}
public void testDelete( ) {
//first make sure the person is in the array
person = myBook.search( Ms. X2 );
if (person == null) {
System.out.println(Error: Didn't find the person it should);
} else {
System.out.println(person.getName() +  is found okay.);
boolean success = myBook.delete(Ms. X2);
if (success) {
person = myBook.search(Ms. X2);
if (person == null) {
System.out.println(Okay: Deletion works);
} else {
wu23399_ch10.qxd 12/28/06 12:39 Page 611
10.7 Sample Development—continued
System.out.println(Error: Person is still there);
}
} else {
System.out.println(Error: Deletion has a problem);
}
}
}
}
612 Chapter 10 Arrays and Collections
Modify the method body of setupArray and testDelete as necessary to test all
other cases described in the step 4 test data table.
Step 5 Development: Finalize
As always, we finalize the program in the last step.We perform a critical program review
to find any inconsistency or error in the methods, incomplete methods, places to add
comments,and so forth.
Since the three operations of add, delete, and search are interrelated, it is critical
to test these operations together. The test program should try out various combinations
of add,delete, and search operations to verify that they work together correctly.
After we complete the class implementation and testing, we may consider
improvement or extension. In addition to the several alternative designs, it is possible
to add other operations. For example, we may want to add an operation to modify a
Person object. Another common operation that is useful in manipulating a collection
of objects is the traversal operation described in Section 10.6. Implementation of this
operation is left as Exercise 16.
prograrn
review
final test
• An array is an indexed collection of data values.
• Data values in an array are called array elements.
• Individual elements in an array are accessed by the indexed expression.
• Array elements can be values of primitive data type or objects.
• In Java, an array can include only elements of the same data type.
• A Java array is a reference data type.
• A Java array is created with the new operator.
• An array can have multiple indices.
S u m m a r y
wu23399_ch10.qxd 12/28/06 12:39 Page 612
• When an array is passed to a method as an argument, only a reference to an
array is passed. A copy of an array is not created. Note: The reference to an
array we are passing is a value of an array variable, and therefore the call-
by-value scheme is used here also.
• The standard classes and interfaces described or used in this chapter are
List
ArrayList
LinkedList
Exercises 613
Iterator
Map
HashMap
TreeMap
• The Java Collection Framework includes many data structure classes such as
lists and maps.
• The List interface represents a linear ordered collection of objects.
• The ArrayList and LinkedList classes are two implementations of the List
interface.
• The Map interface represents a collection of key-value pairs.
• The TreeMap and HashMap classes are two implementations of the Map
interface.
K e y C o n c e p t s
arrays arrays of objects
array elements multidimensional arrays
index expression lists
arrays of primitive data type maps
E x e r c i s e s
1. Identify problems with this code:
public int searchAccount( int[25] number ) {
number = new int[15];
for (int i = 0; i  number.length; i++) {
number[i] = number[i-1] + number[i+1];
}
return number;
}
2. Declare an array of double of size 365 to store daily temperatures for one
year. Using this data structure, write the code to find
• The hottest and coldest days of the year.
• The average temperature of each month.
wu23399_ch10.qxd 12/28/06 12:39 Page 613
• The difference between the hottest and coldest days of every month.
• The temperature of any given day. The day is specified by two input
values: month (1, . . . , 12) and day (1, . . . , 31). Reject invalid
input values (e.g., 13 for month and 32 for day).
3. Repeat Exercise 2, using a two-dimensional array of double with 12 rows
and each row having 28, 30, or 31 columns.
4. Repeat Exercise 2, using an array of Month objects with each Month object
having an array of double of size 28, 30, or 31.
5. For Exercises 2 to 4, the following three data structures are used:
• One-dimensional array of double of size 365.
• Two-dimensional array of double with 12 rows. Each row has 28, 30,
or 31 columns.
• An array of Month objects with each Month object having an array of
double of size 28, 30, or 31.
Discuss the pros and cons of each approach.
6. Suppose you want to maintain the highest and lowest temperatures for every
day of the year. What kind of data structure would you use? Describe the
alternatives and list their pros and cons.
7. If a car dealer’s service department wants a program to keep track of
customer appointments, which data structure should they choose, an array or
a list? If the number of appointments the service department accepts is fixed
on any given day, which data structure is appropriate? What are the criteria
you use to decide which data structure to use? Explain.
8. In Figure 10.8, the last statement
person[last] = null;
is significant. Show the state-of-memory diagram when the last statement is
not executed.
9. Write an application that computes the standard deviation of N real numbers.
The standard deviation s is computed according to
s 

The variable x
 is the average of N input values x1 through xN. The program
first prompts the user for N and then declares an array of size N.
10. Using the payScaleTable two-dimensional array from Section 10.4, write the
code to find
• The average pay for every grade level
• The average pay for every step (i.e., average of every column)
11. Declare a two-dimensional array for the tuition table shown in Figure 10.13.
(x1  x
)2
(x2  x
)2
. . . (xN  x
)2

N
614 Chapter 10 Arrays and Collections
wu23399_ch10.qxd 12/28/06 12:39 Page 614
12. Suppose you want to maintain information on the location where a product
is stored in a warehouse. Would you use a three-dimensional array such as
location[i][j][k], where i is the warehouse number, j is the aisle number, and
k is the bin number? Or would you define three classes Warehouse, Aisle,
and Bin? Describe the alternatives and list their pros and cons.
13. The search method of the AddressBook class returns only one Person object.
Modify the method so that it will return all Person objects that match the
search criteria. You can use an array to return multiple Person objects.
14. Write new search routines for the AddressBook class. The search method
given in the chapter finds a person with a given name. Add second and
third search methods that find all persons, given an age and a gender,
respectively.
15. Modify the add method of the AddressBook class. The method given in the
chapter does not check for any duplicate names. Modify the method so that
no Person object with a duplicate name is added to the address book.
16. Modify the AddressBook class to allow the programmer to access all Person
objects in the address book. Make this modification by adding two methods:
getFirstPerson and getNextPerson. The getFirstPerson method returns the
first Person object in the book. The getNextPerson method returns the next
Person object if there is one. If there is no next person in the book,
getNextPerson returns null. The getFirstPerson method must be called before
the getNextPerson method is called.
17. In addition to the List and Map interface, the third interface in the Java
Collection Framework is Set. A Set is an unordered collection of objects with
no duplicates. This interface models, as expected, the mathematical set. Two
classes that implement the Set interface in JCF are TreeSet and HashSet.
Here’s a simple example of using Set:
SetString = new HashSet String();
set.add(ape);
set.add(bee);
set.add(ape); //duplicate, so it won't be added
set.add(cat);
set.remove(bee);
set.remove(dog); //not in the set, nothing happens
System.out.println(Set =  + set);
The output from the code will be
Set = [ape, cat]
To access the individual elements of a set, call the iterator method in the
manner identical to the one we used for the List interface.
Rewrite the AddressBook class by using the HashSet instead of an
array to maintain a collection of Person object.
Exercises 615
wu23399_ch10.qxd 12/28/06 12:39 Page 615
18. Consider the following Thesaurus class:
class Thesaurus {
//Returns all synonyms of the word as a Set
//Returns null if there is no such word
public java.util.SetString get (String word){...}
//Returns all key words in this thesaurus as a Set
//returns an empty set if there are no keys (if you
//don't do anything, default behavior of the
//underlying JCF class will handle it)
public java.util.SetString keys( ){...}
//Adds 'synonym' to the synonym set of 'word'
//Pay close attention to this method.
public void put (String word, String synonym){...}
}
The get method returns a set of all synonyms of a given word. The keys
method returns all key words in the thesaurus. The put method adds a
new synonym to the given word. Make sure to handle the cases when
the word already has a synonym list and when the word is added for the
first time. Using this Thesaurus class, we can write, for example, this
program:
class SampleMain {
public static void main(String[] args) {
Thesaurus t = new Thesaurus();
t.put(fast, speedy);
t.put(fast, swift);
t.put(slow, sluggish);
SetString synonyms = t.get(fast);
System.out.println(synonyms);
System.out.println(t.keys());
}
}
When the sample program is executed, the output will be
Implement the Thesaurus class, using one of the Map classes. The key is the
word, and the value is the set of synonyms of this word.
616 Chapter 10 Arrays and Collections
wu23399_ch10.qxd 12/28/06 12:39 Page 616
Development Exercises
For the following exercises, use the incremental development methodology to
implement the program. For each exercise, identify the program tasks, create a
design document with class descriptions, and draw the program diagram. Map
out the development steps at the start. Present any design alternatives and
justify your selection. Be sure to perform adequate testing at the end of each
development step.
19. Write a complete address book maintenance application. The user of the
program has four options: Add a new person, delete a person, modify the
data of a person, and search for a person by giving the name. Use the
AddressBook class, either the original one from the chapter or the modified
one from the previous exercises. You have to decide how to allow the user
to enter the values for a new person, display person information, and so
forth.
20. Design a currency converter class whose instance will handle conversion of
all currencies. A single instance of the new currency converter class you
design here will handle all currencies. Instead of having specific conversion
methods such as toDollar, toYen, and so forth, the new currency converter
class supports one generic conversion method called exchange. The method
has three arguments: fromCurrency, toCurrency, and amount. The first two
arguments are String and give the names of currencies. The third argument is
float. To convert $250 to yen, we write
yen = converter.exchange( dollar, yen, 250.0 );
To set the exchange rate for a currency, we use the setRate method. This
method takes two arguments: The first argument is the currency name, and
the second argument is the rate. For example, if the exchange rate for yen is
140 yen to $1, then we write
converter.setRate( yen, 140.0 );
Use an array to keep track of exchange rates.
21. Extend the MyJava Lo-Fat Burgers drive-through ordering system of
Exercise 24 on page 294 so the program can output sales figures. For each
item on the menu, the program keeps track of the sales. At closing time,
the program will output the sales figure in a format similar to the
following:
Item Sales Count Total
Tofu Burger 25 $ 87.25
Cajun Chicken 30 $ 137.70
...
Today's Total Sales: $ 2761.20
Exercises 617
wu23399_ch10.qxd 12/28/06 12:39 Page 617
22. Redo the watermelon projectile computing program of Exercise 30 on
page 362 to output the average distance covered between each time interval.
Use the expression
(x2  x
1)2
 (
y2  y
1)2

to compute the distance between two coordinate points (x1, y1) and (x2, y2).
23. Redo the social club program of Exercise 9 of Chapter 8. In the original
program, we limit the number of clubs to 5. Remove this restriction by using
an array.
24. Redo Exercise 23, but this time use one of the Java Collection Framework
classes.
618 Chapter 10 Arrays and Collections
wu23399_ch10.qxd 12/28/06 12:39 Page 618
Sorting and Searching
O b j e c t i v e s
After you have read and studied this chapter,you should be able to
• Perform linear and binary search algorithms on
arrays.
• Determine whether a linear or binary search is
more effective for a given situation.
• Perform selection and bubble sort algorithms.
• Describe the heapsort algorithm and show
how its performance is superior to that of the
other two algorithms.
• Apply basic sorting algorithms to sort an
array of objects,using the Comparator
interface.
• Define the interface to specify common
behavior and provide different versions of
classes that implement the interface.
619
11
wu23399_ch11.qxd 12/28/06 12:41 Page 619
n this chapter, we cover searching and sorting. In Chapter 10, we presented a case
study of maintaining an address book and described a basic searching method to
locate a student, given his or her name. In this chapter, we will present a better search-
ing algorithm called binary search. To apply binary search, an array must be sorted.
Sorting is a technique to arrange elements in some order, and it is one of the funda-
mental operations we study in computer science. We will cover basic sorting algo-
rithms in this chapter and an efficient recursive sorting algorithm in Chapter 15. We
will use an array of integers to illustrate searching and sorting algorithms, but all the
techniques we present here are equally applicable to any array of objects as well as
primitive data types. In the sample development (Section 11.4), we will extend the
AddressBook class by adding the capability of sorting an array of Person objects.
11.1 Searching
Let’s start with the problem statement for searching:
Given a value X, return the index of X in the array, if such X exists.
Otherwise, return NOT_FOUND (1). We assume there are no duplicate
entries in the array.
There are two possible outcomes in searching: Either we locate an X or we don’t.
We will call the first a successful search and the latter an unsuccessful search.
Figure 11.1 illustrates the successful and unsuccessful searches. As obvious as this
may sound, it is critical to differentiate the two because it is possible for one search-
ing algorithm to perform superbly for successful searches, but very poorly for
unsuccessful searches. When we analyze the performance of a searching algorithm,
we normally derive two separate performances, one for a successful search and
another for an unsuccessful search.
Linear Search
The search technique we used earlier in the book is called a linear search because
we search the array from the first position to the last position in a linear progression.
620 Chapter 11 Sorting and Searching
I n t r o d u c t i o n
I
successful and
unsuccessful
searches
linear search
0 1 2 3 4 5 6 7 8
number
23 17 5 90 12 44 38 84 77
Unsuccessful search:
Successful search:
search(45) NOT_FOUND (1)
search(12) 4
Figure 11.1 Successful and unsuccessful searches.
wu23399_ch11.qxd 12/28/06 12:41 Page 620
The linear search is also called a sequential search. The linear search algorithm can
be expressed as
public int linearSearch (int[] number, int searchValue) {
int loc = 0;
while ( loc  number.length 
number[loc] != searchValue ) {
loc++;
}
if ( loc == number.length) { //Not found
return NOT_FOUND;
} else {
return loc; //Found, return the position
}
}
If the number of entries in the array is N, then there will be N comparisons for
an unsuccessful search (i.e., you search for a value not in the array). In the case of a
successful search, there will be a minimum of one comparison and a maximum of
N comparisons. On average, there will be approximately N2 comparisons.
Is there a way to improve the linear search? If the array is sorted, then we can
improve the search routine by using the binary search technique.
Binary Search
If the values in the array are arranged in ascending or descending order, then we
call the array sorted. In the following explanation of the binary search, we assume
the array is sorted in ascending order. The crux of binary search is the winning
strategy you apply for the Hi-Lo game. When you try to guess a secret number,
say, between 1 and 100, your first guess will be 50. If your guess is HI, then
you know the secret number is between 1 and 49. If your guess is LO, then you
know the secret number is between 51 and 100. By guessing once, you eliminated
one-half of the possible range of values from further consideration. This is the core
idea of binary search.
Consider the following sorted array:
Let’s suppose we are searching for 77. We first search the middle position of the
array. Since this array has 9 elements, the index of the middle position is 4, so we
search number[4]. The value 77 is not in this position. Since 77 is larger than 38 and
0 1 2 3 4 5 6 7 8
number
5 12 17 23 38 44 77 84 90
11.1 Searching 621
More elements
to search
searchValue is
not yet found.
binary search
wu23399_ch11.qxd 12/28/06 12:41 Page 621
the array is sorted, we know that if 77 is in the array, it must be in the right half of
the array. So next we search the middle position of the right half of the array, which
is position 6. Figure 11.2 illustrates the effect of making one comparison in the
binary search.
The search value 77 was found after two comparisons. In contrast, the linear
search would take seven comparisons to locate 77. So there is a net reduction of five
comparisons. How good is the binary search in general? Let’s study the worst-case
situation. In the binary search, after we make one comparison, we can eliminate
one-half of the array from further consideration. So the number of comparisons in
the worst case is the number of times you can divide the array into halves. Suppose
the original size of an array is N and the value we are searching for is not in the
array. Then after one comparison, we have to search the remaining N2 elements.
After two comparisons, we have to search N4 elements, and so on. The following
table shows this relationship. The left column is the number of comparisons, and
the right column is the number of elements we still need to search after making K
comparisons.
Number of Comparisons Number of Elements
0 N
1 N2  N21
2 N4 = N22
. . . . . .
K N2K
622 Chapter 11 Sorting and Searching
0 1 2 3 4 5 6 7 8
5 12 17 23 38 44 77 84 90
0 1 2 3 4 5 6 7 8
44 77 84 90
Search
Search
No need to consider
the left half anymore.
77 must be in this
half of the array.
number[i]  38 38  number[i]
Figure 11.2 Effect of one comparison in binary search.
wu23399_ch11.qxd 12/28/06 12:41 Page 622
The maximum number of comparisons K is derived by solving the equation
N  2K
log2 N  K
This is a remarkable improvement. If the size of the original array is 2048, for exam-
ple, then the unsuccessful binary search takes at most log2 2048  11 comparisons,
while the unsuccessful linear search takes 2048 comparisons. The difference be-
tween the two algorithms gets larger and larger as the size of an array increases.
Now let’s write a binary search method. The key point in the method revolves
on how to stop the search. If the search value is in the array, we will eventually
locate it, so the stopping condition for the successful search is easy. What about the
case for an unsuccessful search? How can we detect that there are no more elements
in the array to search for? Should we use some kind of a counter? We certainly can
use a counter, but we can implement the method without using any counter. To com-
pute the middle location for the next comparison, we need two indices—low and
high. The low and high indices are initialized to 0 and N  1, respectively. The
middle location is computed as
mid = (low + high) / 2; //the result is truncated
If number[mid] is less than the search value, then low is reset to mid+1. If
number[mid] is greater than the search value, then high is reset to mid1, and the
search continues. Eventually, we will locate the search value or we will run out of
elements to compare. We know that there are no more elements to compare when
low becomes larger than high. Figure 11.3 shows how this works.
Here’s the binarySearch method:
public int binarySearch (int[] number, int searchValue) {
int low = 0,
high = number.length - 1,
mid = (low + high) / 2;
while (low = high  number[mid] != searchValue) {
if (number[mid]  searchValue) {
low = mid + 1;
} else { //number[mid]  searchValue
high = mid - 1;
}
mid = (low + high) / 2;
}
if (low  high) {
mid = NOT_FOUND;
}
return mid;
}
11.1 Searching 623
wu23399_ch11.qxd 12/28/06 12:41 Page 623
624 Chapter 11 Sorting and Searching
0 1 2 3 4 5 6 7 8
5 12 17 23 38 44 77 84 90
low0 high8
mid4
38  45
so set low  mid1
1
0 1 2 3 4 5 6 7 8
44 77 84 90
low5 high8
mid6
77  45
so set high  mid1
2
0 1 2 3 4 5 6 7 8
44
high5
low5
mid5
44  45
so set low  mid1
3
0 1 2 3 4 5 6 7 8
low6 high5
low  high
so no more elements to search
4
Suppose we search for 45:
Figure 11.3 How the unsuccessful search is terminated in the binary search routine.
1. Suppose an array contains 2048 elements. What are the least and the greatest
numbers of comparisons for a successful search using linear search?
2. Repeat question 1 for a binary search.
11.2 Sorting
In this section we will describe two basic sorting algorithms. A more advanced
sorting algorithm will be presented in Section 11.3. Let’s start with the problem
statement for sorting:
Given an array of N values, arrange the values into ascending order.
Selection Sort
Given a list of integers, how would you sort them? The most natural sorting algo-
rithm for a human looks something like this:
1. Find the smallest integer in the list.
2. Cross out the number from further consideration and copy it to a new
(sorted) list.
3. Repeat steps 1 and 2 until all numbers are crossed out in the list.
wu23399_ch11.qxd 12/28/06 12:41 Page 624
Figure 11.4 shows this human sorting algorithm with the first three numbers being
copied to the new list.
We can write a real computer program based on this sorting algorithm, but the
resulting program will not be a good one. There are two problems. First, we need an
extra array to keep the sorted list. This may not sound like much, but when you con-
sider an array of, say, 10,000 elements, using a second array is very wasteful. Sec-
ond, crossing out numbers is effective for humans only. We humans can see the
cross marks and will not consider the numbers once they are crossed out, but in
computer programs, we still have to write code to check every element to see
whether it is crossed out. We can “cross out” an element by replacing it with a neg-
ative number, say, 1, if the numbers are all positive. If not, then we have to use
other means to cross out an element. So crossing out the numbers does not reduce
the number of comparisons in the program.
Although we do not want to implement this human sorting algorithm as is, we
can derive an algorithm based on the idea of finding the smallest number in a given
list and moving it to the correct position in the list. This sorting algorithm is called
selection sort.
The selection sort is comprised of sorting passes. Figure 11.5 shows the effect
of the first pass on a sample array of N ( 9) elements. First we locate the smallest
11.2 Sorting 625
0 1 2 3 4 5 6 7 8
23 17 5 90 12 44 38 84 77
0 1 2 3 4 5 6 7 8
5 12 17
Sorted list
Original list
Figure 11.4 Human sorting algorithm after three numbers are moved to the sorted list.
selection sort
sorting passes
0 1 2 3 4 5 6 7 8
5 17
Sorted Unsorted
23 90 12 44 38 84 77
0 1 2 3 4 5 6 7 8
23 17 5 90 12 44 38 84 77
Exchange
start min
Figure 11.5 Effect of executing the first pass in the selection sort.
wu23399_ch11.qxd 12/28/06 12:41 Page 625
element in the array and set the index min to point to this element. Then we
exchange number[start] and number[min]. After the first pass, the smallest element
is moved to the correct position. We increment the value of start by 1 and then
execute the second pass. We start the first pass with start  0 and end the last pass
with start  N-2. Figure 11.6 shows the sequence of eight passes made to the
sample array.
Here’s the selectionSort method:
public void selectionSort(int[] number) {
int minIndex, length, temp;
length = number.length;
for (int startIndex = 0; startIndex = length-2;
startIndex++){
//each iteration of the for loop is one pass
minIndex = startIndex;
//find the smallest in this pass at
//position minIndex
for (int i = startIndex+1; i = length-1; i++) {
if (number[i]  number[minIndex]) minIndex = i;
}
626 Chapter 11 Sorting and Searching
0 1 2 3 4 5 6 7 8
23 17 5 90 12 44 38 84 77
1
0 1 2 3 4 5 6 7 8
5 17 23 90 12 44 38 84 77
2
0 1 2 3 4 5 6 7 8
5 12 23 90 17 44 38 84 77
3
0 1 2 3 4 5 6 7 8
5 12 17 90 23 44 38 84 77
4
Pass
Sorted
0 1 2 3 4 5 6 7 8
5 12 17 23 90 44 38 84 77
5
0 1 2 3 4 5 6 7 8
5 12 17 23 38 44 90 84 77
6
0 1 2 3 4 5 6 7 8
5 12 17 23 38 44 90 84 77
7
0 1 2 3 4 5 6 7 8
5 12 17 23 38 44 77 84 90
8
Pass
Figure 11.6 Eight passes to sort the sample array of nine elements.
wu23399_ch11.qxd 12/28/06 12:41 Page 626
//exchange number[startIndex] and number[minIndex]
temp = number[startIndex];
number[startIndex] = number[minIndex];
number[minIndex] = temp;
assert minStart(number, startIndex):
Error:  + number[startIndex] +
 at position  + startIndex +
 is not the smallest.;
}
assert isSorted(number):
Error: the final is not sorted;
}
The assertion at the end of one pass confirms that the smallest element in that pass
moved to the beginning position of the pass. The minStart method is therefore
written as follows:
private boolean minStart(int[] number, int startIndex) {
for (int i = startIndex+1; i  number.length; i++) {
if (number[startIndex]  number[i]) {
return false;
}
}
return true;
}
We put a second assertion at the end of the method to verify that no elements
are out of place after the sorting is complete. The isSorted method is written as
follows:
private boolean isSorted(int[] number) {
for (int i = 0; i  number.length-1; i++) {
if (number[i]  number[i+1]) {
return false;
}
}
return true;
}
Assertion is a very useful tool in a situation such as sorting. While developing
the sorting routines, we insert a number of assertion statements to increase our con-
fidence in the program’s correctness.
11.2 Sorting 627
wu23399_ch11.qxd 12/28/06 12:41 Page 627
Let’s analyze the selection sort algorithm. In analyzing different sorting algo-
rithms, we normally count two things: the number of comparisons and the number
of data movements (exchanges). We will show you how to count the number of
comparisons here. Counting the number of data movements is left as Exercise 4.
Keep in mind that the analysis we provide in this chapter is an informal one. A
detailed analysis is beyond the scope of this book, so we will give only a taste of
the formal analysis.
The selection sort has one comparison (the if statement inside the nested-for
loop), so we can easily count the total number of comparisons by counting the num-
ber of times the inner loop is executed. For each execution of the outer loop, the
inner loop is executed length  start times. The variable start ranges from 0 to
length-2. So the total number of comparisons is computed by finding the sum of the
right column in the following table:
Number of Comparisons
Start (Length  Start)
0 length
1 length – 1
2 length – 2
. . . . . .
length – 2 2
628 Chapter 11 Sorting and Searching
Be sure to run programs with assertions enabled during the development,
but disable them during the actual use.Run the program with assertions
enabled with
java -ea main class
We use assertions to find coding error. But what will happen when the code
we write for assertions, such at the minStart method used in the selection
sort routine, is wrong? How can we assert that minStart is correct? We do not
want to write assertions for assertions! One possibility is to create a data set
that is correct and to run the minStart method against these data to test for
their validity. The use of assertions is merely an aid, not a fail-safe way to find
errors.
wu23399_ch11.qxd 12/28/06 12:41 Page 628
The variable length is the size of the array. If we replace length with N, the
size of the array, then the sum of the right column is
N  (N  1)  (N  2)  . . .  2  
N
i2
i 

N
i1
i
 1
 
N(N
2
 1)
  1  
N2

2
N 2
  N2
The total number of comparisons is approximately the square of the size of an
array. This is a quadratic function, so the number of comparisons grows very
rapidly as the size of an array increases. Is there a better sorting algorithm? The
answer is yes.
Bubble Sort
The effect of one pass of the selection sort is the movement of the smallest element
to its correct position. Since an array gets sorted only by moving the elements to
their correct positions, the whole sorting routine will complete sooner if we increase
the number of data movements. In the selection sort, we make one exchange per
pass. If we could move more elements toward their correct positions in one pass, we
could complete the sorting sooner than the selection sort. The bubble sort is one
such algorithm that increases the number of data movements for the same number
of comparisons as the selection sort makes.
The key point of the bubble sort is to make pairwise comparisons and to ex-
change the positions of the pair if they are out of order. Figure 11.7 shows the effect
of pairwise comparisons in the first pass of the bubble sort. After the first pass, the
largest element, 90, has moved to its correct position in the array. This is the guar-
anteed effect of one pass. In addition, we notice that many other elements have
moved toward their correct positions, as bubbles move toward the water’s surface.
In the worst case, the bubble sort will make N  1 passes, so the worst-case
performance is the same as that for the selection sort. However, in the average case,
we can expect a better performance from the bubble sort. The bubble sort exhibits
two properties:
• After one pass through the array, the largest element will be at the end of the
array.
• During one pass, if no pair of consecutive entries is out of order, then the
array is sorted.
Using these properties, we can express the bubbleSort method in pseudocode:
bottom = number.length - 2;
exchanged = true;
while (exchanged) { //continue if the exchange is made
//do one pass of sorting
exchanged = false; //reset the variable
11.2 Sorting 629
This while loop per-
forms at most N1
passes for an array
with N elements.The
loop will terminate
when there are no ex-
changes in one pass.
wu23399_ch11.qxd 12/28/06 12:41 Page 629
630 Chapter 11 Sorting and Searching
0 1 2 3 4 5 6 7 8
23 17 5 90 12 44 38 84 77
0 1 2 3 4 5 6 7 8
17 23 5 90 12 44 38 84 77
0 1 2 3 4 5 6 7 8
17 5 23 90 12 44 38 84 77
Exchange
Exchange
0 1 2 3 4 5 6 7 8
17 5 23 12 90 44 38 84 77
Exchange
0 1 2 3 4 5 6 7 8
17 5 23 12 44 90 38 84 77
Exchange
0 1 2 3 4 5 6 7 8
17 5 23 12 44 38 90 84 77
Exchange
0 1 2 3 4 5 6 7 8
17 5 23 12 44 38 84 90 77
Exchange
0 1 2 3 4 5 6 7 8
17 5 23 12 44 38 84 77 90
Exchange
Notice how the value 90
migrates toward its correct
position. In addition, other
values also move toward
their correct positions.
Effect: The largest element
moves to its correct position.
Use an assertion to verify
this condition.
Figure 11.7 Effect of executing the first pass in the bubble sort.
wu23399_ch11.qxd 12/28/06 12:41 Page 630
for (int i = 0; i = bottom; i++) { //pairwise comparison
if (number[i]  number[i+1]) {
//the pair is out of order
exchange them;
exchanged = true; //an exchange is made
}
}
//one pass is done, decrement the bottom index by 1
bottom--;
}
Translating the pseudocode into an actual method, we have
public void bubbleSort(int[] number) {
int temp, bottom;
boolean exchanged = true;
bottom = number.length - 2;
while (exchanged) {
exchanged = false;
for (int i = 0; i = bottom; i++) {
if (number[i]  number[i+1]) {
temp = number[i]; //exchange
number[i] = number[i+1];
number[i+1] = temp;
exchanged = true; //exchange is made
}
}
assert maxBottom(number, bottom):
Error:  + number[bottom] +
 at position  + bottom +
 is not the largest.;
bottom--;
}
assert isSorted(number):
Error: the final is not sorted;
}
The maxBottom method verifies that the largest element among elements from
position 0 to bottom is at position bottom. The method is written as follows:
private boolean maxBottom(int[] number, int lastIndex) {
for (int i = 0; i  lastIndex; i++) {
11.2 Sorting 631
One pass of
bubble sort
Assert the element at posi-
tion bottom is the largest
among elements from
position 0 to bottom.
wu23399_ch11.qxd 12/28/06 12:41 Page 631
if (number[lastIndex]  number[i]) {
return false;
}
}
return true;
}
On average, we expect the bubble sort to finish sorting sooner than the selec-
tion sort, because there will be more data movements for the same number of
comparisons, and there is a test to exit the method when the array gets sorted. The
worst case of the bubble sort happens when the original array is in descending
order. Notice that if the original array is already sorted, the bubble sort will perform
only one pass whereas the selection sort will perform N  1 passes.
632 Chapter 11 Sorting and Searching
1. Show the result of the second pass of bubble sort applied to the array at the
bottom of Figure 11.7.
2. For an array with N elements, what is the least number of comparisons the
bubble sort will execute?
11.3 Heapsort
Selection and bubble sorts are two fundamental sorting algorithms that take
approximately N2
comparisons to sort an array of N elements. One interesting
sorting algorithm that improves this performance to approximately 1.5N log2 N
is heapsort. We will describe the heapsort algorithm and analyze its performance in
this section.
The heapsort algorithm uses a special data structure called a heap. A heap
consists of nodes, which contain data values, and edges, which link the nodes.
Figure 11.8 shows a sample heap. We use integers as data values for the examples
in this section. The topmost node is called the root node of a heap. Nodes in a heap
heapsort
heap
root node
90
84 44
77 12 5 38
17 23
7
3 4 5 6
1 2
0
8
Index
Root
Left child
of 44
Right child
of 44
Figure 11.8 A sample heap that includes nine nodes.
wu23399_ch11.qxd 12/28/06 12:41 Page 632
are indexed 0, 1, 2, and so forth in the top-to-bottom, left-to-right order, starting
from the root. A node in a heap has zero, one, or two children. The children of a
node are distinguished as the node’s left and right children. If a node has only one
child, then it is the left child of the node.
A heap must satisfy these two constraints:
1. Structural constraint. Nodes in a heap with N nodes must occupy the
positions numbered 0, 1, . . . , N  1. Figure 11.9 shows examples of
nonheaps that violate the structural constraint.
2. Value relationship constraint. A value stored in a node must be larger than
the maximum of the values stored in its left and right children. Figure 11.10
shows examples of nonheaps that violate the value relationship constraint.
11.3 Heapsort 633
left and right
children
heap
constraints
Heaps
Nonheaps
Figure 11.9 Sample heaps and nonheaps that violate the structural constraint.
45
22
16
12
3
45
25
45
9
34
22
11
45
12
45
12
24
16
13
90
35
45
58
16
12
3
45
55
45
22
33
23
34
45
25
45
22
55
12
3
45
25
Heaps
Nonheaps
Figure 11.10 Sample heaps and nonheaps that violate the value relationship constraint.Violations are
indicated by blue ellipses.
wu23399_ch11.qxd 12/28/06 12:41 Page 633
How can we use the heap structure to sort N elements? Heapsort is carried out
in two phases:
1. Construction phase. Construct a heap given N elements.
2. Extraction phase. Pull out the value in the root successively, creating a new
heap with one less element after each extraction step.
We will begin the description of heapsort from the extraction phase. Consider the
heap shown in Figure 11.8. Since every node in the heap satisfies the value rela-
tionship constraint, we can conclude that the value in the root node is the largest
among the values stored in the heap. Now, after we remove the value 90 from the
heap, we must create a new heap that has one less element. We can build such a heap
by first moving the last element (value 23 in the figure) to the root position. With the
value 23 in the root position, we have met the structural constraint for the heap with
eight elements. However, the value relationship constraint is not met. The violation
occurs because 23 is smaller than the larger of its two children. By swapping 84 and
23, the violation is eliminated. Since the value 23 is now at a new location, we must
check again if the violation occurs at this position. It does, because 77 is larger than
23, so we swap again. We repeat the process until either there are no more children
to consider or the value moved into a new position meets the value relationship
constraint. We will call this process a rebuild step. One rebuild step is illustrated in
Figure 11.11.
Using a heap with N elements, we can sort the given N elements by perform-
ing the rebuild steps N  1 times. Figure 11.12 illustrates the rebuild steps for the
sample heap. Notice how the array for the sorted list is filled from the end. All
we have to do now is to figure out how to build a heap from a given unsorted list of
N elements. Let’s study the construction phase of the algorithm.
We will illustrate the construction phase with the following unsorted nine
elements:
23, 17, 5, 90, 12, 44, 38, 84, 77
If we assign the given numbers to a heap structure in ascending index order, we
have the heap structure shown in Figure 11.13. This heap structure is not truly a
heap because it violates the value relationship constraint. The construction phase
will eliminate any violations of the value relationship constraint. The key concept
for the construction phase is the rebuild step we used in the extraction phase. We
will build a complete heap in a bottom-up fashion. We start out with a small heap
and gradually build bigger and bigger heaps until the heap contains all N elements.
Figure 11.14 shows the sequence of rebuild steps. The triangles indicate where the
rebuild steps are applied. In the extraction step, the rebuild step is always applied to
the root node. In the construction step, each rebuild step is applied successively, be-
ginning with the node at index (N  2)2 and ending with the node at index 0 (i.e.,
the root node).
Now let’s consider how we can implement this algorithm in Java. We must
first decide how to represent a heap. Among the possible alternatives, the data
structure we learned in this book that can be used here very effectively is an array.
634 Chapter 11 Sorting and Searching
heapsort
phases
rebuild step
wu23399_ch11.qxd 12/28/06 12:41 Page 634
11.3 Heapsort 635
0 1 2 3 4 5 6 7 8
90
M
ove
M
ove
77
7
3 4 5
0
6
8
17
77
17
1 2
84
12 5
44
90
23
38
7
3 4 5
0
6
84
1 2
77
12 5
44
38
7
3 4 5
0
6
23
1 2
84
12 5
44
38
23  max{84, 44} ? NO, so swap
7
3 4 5
0
6
77
17
23
17
23
17
84
1 2
23
12 5
44
38
23  max{77, 12} ? NO, so swap
7
3 4 5
0
6
84
1 2
77
12 5
44
38
23  max{17} ? YES, so stop
A new heap with
one fewer element.
Figure 11.11 One rebuild step after the value 90 is pulled out from the heap. The net result of a single
rebuild step is a new heap that contains one fewer element.
wu23399_ch11.qxd 12/28/06 12:41 Page 635
0 1 2 3 4 5 6 7 8
90
77
7
3 4 5
0
6
8
17 23
1 2
84
12 5
44
38
0 1 2 3 4 5 6 7 8
90
84
23
7
3 4 5
0
6
17
1 2
77
12 5
44
38
0 1 2 3 4 5 6 7 8
90
84
77
17
3 4 5
0
6
1 2
23
12 5
44
38
0 1 2 3 4 5 6 7 8
90
84
77
44
17
3 4 5
0
1 2
23
12 5
38
1
0 1 2 3 4 5 6 7 8
90
84
77
44
38
17
3 4
0
1 2
23
12
5
5
0 1 2 3 4 5 6 7 8
90
84
77
44
38
23
12
3
0
1 2
17 5
6
0 1 2 3 4 5 6 7 8
90
84
77
44
38
23
17
0
1 2
12 5
7
0 1 2 3 4 5 6 7 8
90
84
77
44
38
23
17
12
0
1
5
8
0 1 2 3 4 5 6 7 8
90
84
77
44
38
23
17
12
5
0
9
2
3
4
No rebuild is
necessary here.
90
84
23
17
77
44
12
5
38
Figure 11.12 Eight rebuild steps to sort a heap with nine elements.
636
wu23399_ch11.qxd 12/28/06 12:41 Page 636
Figure 11.15 shows the correspondence between the heap and the array representa-
tion. An important aspect in deciding which data structure to use is the ease of
locating a given node’s left and right children. With an array implementation, we
can locate any node’s left and right children easily. A node with index I has its left
child at index 2I  1 and its right child at index 2I  2.
Since the heapsort algorithm is more involved than the insertion or bubble sort
algorithm, we will put all the necessary code in a single class called Heap to provide
a complete picture. To simplify our implementation so we can focus on the algo-
rithm, we will allow only integers. You can modify the class to allow any objects to
be sorted; see Exercise 9 at the end of this chapter. The following code illustrates
how to use the Heap class:
int[ ] number = { 90, 44, 84, 12, 77, 23, 38, 5, 17 };
int[ ] sortedList;
Heap heap = new Heap( );
heap.setData(number); //assign the original list
sortedList = heap.sort( );//sort the list
for (int i = 0; i  sortedList.length; i++) { //print out
System.out.print(  + sortedList[i]); //the sorted
} //list
The Heap class will include two arrays as its data members: one to implement
a heap and another to store the sorted list.
/**
* This class implements the heapsort algorithm. This class
* can sort only integers.
*/
class Heap {
11.3 Heapsort 637
23
17 5
90 12 44 38
84 77
7
3 4 5 6
1 2
0
8
Figure 11.13 A heap structure with given numbers assigned in ascending index order.
wu23399_ch11.qxd 12/28/06 12:41 Page 637
638 Chapter 11 Sorting and Searching
90
7
3 4 5
0
6
8
1
First rebuild at position 3.
84 77
23
1 2
17
12 44
5
38
90
7
3 4 5
0
6
8
3
Third rebuild at position 1.
84 77
23
1 2
17
12 5
44
38
77
7
3 4 5
0
6
8
17 23
90
1 2
84
12 5
44
38
84
7
3 4 5
0
6
8
4
Fourth rebuild at position 0.
17 77
23
1 2
90
12 5
44
38
90
7
3 4 5
0
6
8
2
Second rebuild at position 2.
84 77
23
1 2
17
12 44
5
38
Figure 11.14 Sequence of rebuild steps applied in the construction phase.Rebuild steps are carried out at
index positions 3,2,1,and 0.
wu23399_ch11.qxd 12/28/06 12:41 Page 638
/**
* Implements the heap
*/
private int[ ] heap;
/**
* Stores the sorted list
*/
private int[ ] sortedList;
// methods come here
...
}
Now let’s look at the methods. The setData method initializes the two data
members as follows:
public void setData(int[ ] data) {
heap = new int[data.length];
sortedList = new int[data.length];
for (int i = 0; i  data.length; i++) {
heap[i] = data[i];
}
}
Notice that we copy the contents of the data array to the heap array. If we simply
assign the parameter to the data member heap as
heap = data;
then all we are doing is setting two names referring to the same object. Since we do
not want to change the original data array, we make a separate copy.
11.3 Heapsort 639
90
Heap Array implementation
84 44
77 12 5 38
17 23
7
3 4 5 6
1 2
0
8
0 1 2 3 4 5 6 7 8
90 84 44 77 12 5 38 17 23
Figure 11.15 A sample heap and the corresponding array implementation.
wu23399_ch11.qxd 12/28/06 12:41 Page 639
The sort method calls two private methods that implement the two phases of
the heapsort algorithm:
public int[ ] sort( ) {
construct( ); //perform the construction phase
extract( ); //perform the extraction phase
return sortedList;
}
Here’s the construct method:
private void construct( ) {
int current, maxChildIndex;
boolean done;
for (int i = (heap.length-2) / 2; i = 0; i--) {
current = i;
done = false;
while (!done) {//perform one rebuild step
//with the node at index i
if (2*current+1  heap.length-1) {
//current node has no children, so stop
done = true;
} else {
//current node has at least one child,
//get the index of larger child
maxChildIndex
= maxChild(current, heap.length-1);
if (heap[current]  heap[maxChildIndex]) {
//a child is larger, so swap and continue
swap(current, maxChildIndex);
current = maxChildIndex;
} else { //the value relationship constraint
//is satisfied, so stop
done = true;
}
}
}
assert isValidHeap(heap, i, heap.length-1):
Error: Construction phase is not working  +
correctly;
}
640 Chapter 11 Sorting and Searching
wu23399_ch11.qxd 12/28/06 12:41 Page 640
testPrint(heap.length ); //TEMP
}
The isValidHeap method is used to assert that elements from position start to posi-
tion end form a valid heap structure. Here’s the method:
private boolean isValidHeap(int[] heap,
int start, int end) {
for (int i = start; i  end/ 2; i++) {
if (heap[i]  Math.max(heap[2*i+1], heap[2*i+2])) {
return false;
}
}
return true;
}
And here’s the extract method:
private void extract( ) {
int current, maxChildIndex;
boolean done;
for (int size = heap.length-1; size = 0; size--) {
//remove the root node data
sortedList[size] = heap[0];
//move the last node to the root
heap[0] = heap[size];
//rebuild the heap with one fewer element
current = 0;
done = false;
while (!done) {
if (2*current+1  size) {
//current node has no children, so stop
done = true;
} else {
//current node has at least one child,
//get the index of larger child
maxChildIndex = maxChild(current, size);
if (heap[current]  heap[maxChildIndex]) {
//a child is larger, so swap and continue
swap(current, maxChildIndex);
current = maxChildIndex;
11.3 Heapsort 641
wu23399_ch11.qxd 12/28/06 12:41 Page 641
} else { //value relationship constraint
//is satisfied, so stop
done = true;
}
}
}
assert isValidHeap(heap, i, heap.length-1):
Error: Construction phase is not working  +
correctly;
testPrint( size ); //TEMP
}
}
A number of methods are shared by both methods. The maxChild method
returns the index of a node’s left or right child, whichever is larger. This method
is called only if a node has at least one child. The first parameter is the index of a
node, and the second parameter is the index of the last node in a heap. The second
parameter is necessary to determine whether a node has a right child. The method
is defined as follows:
private int maxChild(int location, int end) {
int result, leftChildIndex, rightChildIndex;
rightChildIndex = 2*location + 2;
leftChildIndex = 2*location + 1;
//Precondition:
// Node at 'location' has at least one child
assert leftChildIndex = end:
Error: node at position  + location +
has no children.;
if (rightChildIndex = end 
heap[leftChildIndex]  heap[rightChildIndex]) {
result = rightChildIndex;
} else {
result = leftChildIndex;
}
return result;
}
The other two methods shared by the construct and extract methods are swap
and testPrint. The swap method interchanges the contents of two array elements,
and the testPrint method outputs the heap array for verification and debugging pur-
poses. You can comment out the calls to testPrint from the construct and extract
642 Chapter 11 Sorting and Searching
wu23399_ch11.qxd 12/28/06 12:41 Page 642
methods after you verify that the algorithm is implemented correctly. Here are the
two methods:
private void swap (int loc1, int loc2) {
int temp;
temp = heap[loc1];
heap[loc1] = heap[loc2];
heap[loc2] = temp;
}
private void testPrint(int limit) {
for (int i = 0; i  limit; i++) {
System.out.print(  + heap[i]);
}
System.out.println( );
}
There are several improvements we can make to the simple Heap class we
provided here. These improvements are left as Exercise 9.
Performance
How good is the heapsort? We mentioned in Section 11.2 that the performances of
both selection and bubble sort algorithms are approximately N2
comparisons for
sorting N elements. The heapsort algorithm is substantially more complex in
design and implementation than the other two basic sorting algorithms. Is the extra
complexity worth our effort? The answer is yes. The performance of the heapsort
algorithm is approximately 1.5N log2 N comparisons for sorting N elements.
This is a remarkable improvement. Consider the difference between the two per-
formances for large N. For example, to sort 100,000 elements, the selection or
bubble sort requires 10,000,000,000 comparisons, while the heapsort requires only
1.5 100,000 log2 100,000  2,491,695 comparisons. If a single comparison
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf
A Comprehensive Introduction to Object-Oriented Programming with Java.pdf

More Related Content

PDF
A Comprehensive Introduction To Objectoriented Programming With Java C Thomas Wu
PDF
(eBook PDF) Introduction to Programming with Java: A Problem Solving Approach...
PDF
(eBook PDF) Introduction to Programming with Java: A Problem Solving Approach...
PDF
(eBook PDF) Introduction to Programming with Java: A Problem Solving Approach...
PDF
Core Java An Integrated Approach 2nd R Nageswara Rao
PDF
Core Java Volume I Fundamentals 12th Horstmann Cay
PDF
Objectoriented Programming And Java 2nd Ed Danny C C Poo Derek Beng Kee Kiong...
PDF
Hp syllabus
A Comprehensive Introduction To Objectoriented Programming With Java C Thomas Wu
(eBook PDF) Introduction to Programming with Java: A Problem Solving Approach...
(eBook PDF) Introduction to Programming with Java: A Problem Solving Approach...
(eBook PDF) Introduction to Programming with Java: A Problem Solving Approach...
Core Java An Integrated Approach 2nd R Nageswara Rao
Core Java Volume I Fundamentals 12th Horstmann Cay
Objectoriented Programming And Java 2nd Ed Danny C C Poo Derek Beng Kee Kiong...
Hp syllabus

Similar to A Comprehensive Introduction to Object-Oriented Programming with Java.pdf (20)

PDF
Download full ebook of Java Cookbook Ian F Darwin Darwin Ian F instant downlo...
PDF
[FREE PDF sample] Object Oriented Programming and Java Second Edition Danny P...
PDF
Java Complete Reference Fifth Edition
PDF
Java Programming.pdf
PDF
Learning Java An Introduction to Real World Programming with Java Marc Loy
PDF
Introduction to Programming with Java 3rd Edition John Dean
PDF
Essential Java For Scientists And Engineers 1st Brian D Hahn
PDF
(eTextbook PDF) for Starting Out with Java: From Control Structures through O...
PPT
PPTX
Java programmingjsjdjdjdjdjdjdjdjdiidiei
PPT
INTRODUCTION TO JAVA
PDF
(eBook PDF) Starting Out with Java: From Control Structures through Objects, ...
PPT
java01.ppt
PPT
java01.pptbvuyvyuvvvvvvvvvvvvvvvvvvvvyft
PPT
Java Simple Introduction in single course
PDF
Learning Java An Introduction to Real World Programming with Java Marc Loy
PDF
java-language-programação.2-PDF Room.pdf
PDF
Core-java-materiallgfffdfdhgfjfghfchgfhg
PPTX
Java tutorial for beginners-tibacademy.in
PDF
Java programming-language-handbook
Download full ebook of Java Cookbook Ian F Darwin Darwin Ian F instant downlo...
[FREE PDF sample] Object Oriented Programming and Java Second Edition Danny P...
Java Complete Reference Fifth Edition
Java Programming.pdf
Learning Java An Introduction to Real World Programming with Java Marc Loy
Introduction to Programming with Java 3rd Edition John Dean
Essential Java For Scientists And Engineers 1st Brian D Hahn
(eTextbook PDF) for Starting Out with Java: From Control Structures through O...
Java programmingjsjdjdjdjdjdjdjdjdiidiei
INTRODUCTION TO JAVA
(eBook PDF) Starting Out with Java: From Control Structures through Objects, ...
java01.ppt
java01.pptbvuyvyuvvvvvvvvvvvvvvvvvvvvyft
Java Simple Introduction in single course
Learning Java An Introduction to Real World Programming with Java Marc Loy
java-language-programação.2-PDF Room.pdf
Core-java-materiallgfffdfdhgfjfghfchgfhg
Java tutorial for beginners-tibacademy.in
Java programming-language-handbook
Ad

More from Yasmine Anino (20)

PDF
Essay On Shylock
PDF
Short Essay On Swami Vivekananda
PDF
Evaluation Essay Samples
PDF
Science Topics For Essays
PDF
Essay Environmental Protection
PDF
College Level Persuasive Essay Topics
PDF
Free Printable Ice Cream Shaped Writing Templates
PDF
Outrageous How To Write A Concl
PDF
Free Printable Essay Writing Worksheets - Printable Wo
PDF
Structure Of An Academic Essay
PDF
Critical Appraisal Example 2020-2022 - Fill And Sign
PDF
Uva 2023 Supplemental Essays 2023 Calendar
PDF
Superhero Writing Worksheet By Jill Katherin
PDF
Printable Writing Paper Vintage Christmas Holly B
PDF
Good Personal Reflective Essay
PDF
Position Paper Mun Examples - Position Paper
PDF
Hook C Lead C Attention Grabber Beginning An Ess
PDF
Princess Themed Writing Paper Teaching Resources
PDF
How To Write Essays Faster - CIPD Students Help
PDF
Research Paper Step By Step Jfk Research Paper Outline Researc
Essay On Shylock
Short Essay On Swami Vivekananda
Evaluation Essay Samples
Science Topics For Essays
Essay Environmental Protection
College Level Persuasive Essay Topics
Free Printable Ice Cream Shaped Writing Templates
Outrageous How To Write A Concl
Free Printable Essay Writing Worksheets - Printable Wo
Structure Of An Academic Essay
Critical Appraisal Example 2020-2022 - Fill And Sign
Uva 2023 Supplemental Essays 2023 Calendar
Superhero Writing Worksheet By Jill Katherin
Printable Writing Paper Vintage Christmas Holly B
Good Personal Reflective Essay
Position Paper Mun Examples - Position Paper
Hook C Lead C Attention Grabber Beginning An Ess
Princess Themed Writing Paper Teaching Resources
How To Write Essays Faster - CIPD Students Help
Research Paper Step By Step Jfk Research Paper Outline Researc
Ad

Recently uploaded (20)

PPTX
human mycosis Human fungal infections are called human mycosis..pptx
PPTX
Revamp in MTO Odoo 18 Inventory - Odoo Slides
PPTX
Cell Structure & Organelles in detailed.
PDF
O7-L3 Supply Chain Operations - ICLT Program
PDF
102 student loan defaulters named and shamed – Is someone you know on the list?
PPTX
Introduction and Scope of Bichemistry.pptx
PPTX
Introduction to Child Health Nursing – Unit I | Child Health Nursing I | B.Sc...
PDF
O5-L3 Freight Transport Ops (International) V1.pdf
PPTX
How to Manage Starshipit in Odoo 18 - Odoo Slides
PPTX
Pharma ospi slides which help in ospi learning
PPTX
Pharmacology of Heart Failure /Pharmacotherapy of CHF
PDF
ANTIBIOTICS.pptx.pdf………………… xxxxxxxxxxxxx
PPTX
IMMUNITY IMMUNITY refers to protection against infection, and the immune syst...
PDF
3rd Neelam Sanjeevareddy Memorial Lecture.pdf
PPTX
Onica Farming 24rsclub profitable farm business
PDF
Saundersa Comprehensive Review for the NCLEX-RN Examination.pdf
PPTX
Open Quiz Monsoon Mind Game Prelims.pptx
PDF
Mga Unang Hakbang Tungo Sa Tao by Joe Vibar Nero.pdf
PDF
The Lost Whites of Pakistan by Jahanzaib Mughal.pdf
PDF
BÀI TẬP BỔ TRỢ 4 KỸ NĂNG TIẾNG ANH 9 GLOBAL SUCCESS - CẢ NĂM - BÁM SÁT FORM Đ...
human mycosis Human fungal infections are called human mycosis..pptx
Revamp in MTO Odoo 18 Inventory - Odoo Slides
Cell Structure & Organelles in detailed.
O7-L3 Supply Chain Operations - ICLT Program
102 student loan defaulters named and shamed – Is someone you know on the list?
Introduction and Scope of Bichemistry.pptx
Introduction to Child Health Nursing – Unit I | Child Health Nursing I | B.Sc...
O5-L3 Freight Transport Ops (International) V1.pdf
How to Manage Starshipit in Odoo 18 - Odoo Slides
Pharma ospi slides which help in ospi learning
Pharmacology of Heart Failure /Pharmacotherapy of CHF
ANTIBIOTICS.pptx.pdf………………… xxxxxxxxxxxxx
IMMUNITY IMMUNITY refers to protection against infection, and the immune syst...
3rd Neelam Sanjeevareddy Memorial Lecture.pdf
Onica Farming 24rsclub profitable farm business
Saundersa Comprehensive Review for the NCLEX-RN Examination.pdf
Open Quiz Monsoon Mind Game Prelims.pptx
Mga Unang Hakbang Tungo Sa Tao by Joe Vibar Nero.pdf
The Lost Whites of Pakistan by Jahanzaib Mughal.pdf
BÀI TẬP BỔ TRỢ 4 KỸ NĂNG TIẾNG ANH 9 GLOBAL SUCCESS - CẢ NĂM - BÁM SÁT FORM Đ...

A Comprehensive Introduction to Object-Oriented Programming with Java.pdf

  • 2. A Comprehensive Introduction to Object-Oriented Programming with JavaTM C.Thomas Wu Naval Postgraduate School wu23399_fm.qxd 1/10/07 11:53 Page i
  • 3. A COMPREHENSIVE INTRODUCTION TO OBJECT-ORIENTED PROGRAMMING WITH JAVA Published by McGraw-Hill, a business unit of The McGraw-Hill Companies, Inc., 1221 Avenue of the Americas, New York, NY 10020. Copyright © 2008 by The McGraw-Hill Companies, Inc. All rights reserved. No part of this publication may be reproduced or distributed in any form or by any means, or stored in a database or retrieval system, without the prior written consent of The McGraw-Hill Companies, Inc., including, but not limited to, in any network or other electronic storage or transmission, or broadcast for distance learning. Some ancillaries, including electronic and print components, may not be available to customers outside the United States. This book is printed on acid-free paper. 1 2 3 4 5 6 7 8 9 0 DOC/DOC 0 9 8 7 ISBN 978–0–07–352339–2 MHID 0–07–352339–9 Publisher: Alan R. Apt Executive Marketing Manager: Michael Weitz Senior Project Manager: Sheila M. Frank Lead Production Supervisor: Sandy Ludovissy Associate Media Producer: Christina Nelson Designer: Rick D. Noel Cover Designer: Elise Lansdon (USE) Cover Image: breaking wave on foaming ocean surface, ®Ron Dahlquist/Getty Images Compositor: ICC Macmillan Inc. Typeface: 10.5/12 Times Roman Printer: R. R. Donnelley Crawfordsville, IN Library of Congress Cataloging-in-Publication Data Wu, C. Thomas. A comprehensive introduction to object-oriented programming with Java / C. Thomas Wu. – 1st ed. p. cm. ISBN 978–0–07–352339–2 — ISBN 0–07–352339–9 1. Object-oriented programming (Computer science) 2. Java (Computer program language) I. Title. QA76.64.W77 2008 005.117–dc22 2006048064 www.mhhe.com wu23399_fm.qxd 1/10/07 11:53 Page ii
  • 4. To my family wu23399_fm.qxd 1/10/07 11:53 Page iii
  • 6. v Preface xiii Key Differences from the Standard Edition xiii Book Organization xiv Hallmark Features of the Text xviii 0 Introduction to Computers and Programming Languages 1 0.1 A History of Computers 2 0.2 Computer Architecture 4 0.3 Programming Languages 11 0.4 Java 12 1 Introduction to Object-Oriented Programming and Software Development 15 1.1 Classes and Objects 16 1.2 Messages and Methods 18 1.3 Class and Instance Data Values 20 1.4 Inheritance 23 1.5 Software Engineering and Software Life Cycle 24 C o n t e n t s wu23399_fm.qxd 1/10/07 11:53 Page v
  • 7. 2 Getting Started with Java 29 2.1 The First Java Program 30 2.2 Program Components 39 2.3 Edit-Compile-Run Cycle 49 2.4 Sample Java Standard Classes 52 2.5 Sample Development 67 3 Numerical Data 81 3.1 Variables 82 3.2 Arithmetic Expressions 90 3.3 Constants 95 3.4 Displaying Numerical Values 97 3.5 Getting Numerical Input 103 3.6 The Math Class 109 3.7 Random Number Generation 113 3.8 The GregorianCalendar Class 115 3.9 Sample Development 120 3.10 Numerical Representation (Optional) 131 4 Defining Your Own Classes—Part 1 145 4.1 First Example:Defining and Using a Class 146 4.2 Second Example:Defining and Using Multiple Classes 156 4.3 Matching Arguments and Parameters 160 4.4 Passing Objects to a Method 162 4.5 Constructors 167 4.6 Information Hiding and Visibility Modifiers 172 4.7 Class Constants 175 4.8 Local Variables 183 4.9 Calling Methods of the Same Class 185 4.10 Changing Any Class to a Main Class 189 4.11 Sample Development 190 vi Contents wu23399_fm.qxd 1/10/07 11:53 Page vi
  • 8. Contents vii 5 Selection Statements 213 5.1 The if Statement 214 5.2 Nested if Statements 225 5.3 Boolean Expressions and Variables 231 5.4 Comparing Objects 239 5.5 The switch Statement 244 5.6 Drawing Graphics 248 5.7 Enumerated Constants 258 5.8 Sample Development 264 6 Repetition Statements 295 6.1 The while Statement 296 6.2 Pitfalls in Writing Repetition Statements 305 6.3 The do–while Statement 311 6.4 Loop-and-a-Half Repetition Control 315 6.5 The for Statement 319 6.6 Nested for Statements 324 6.7 Formatting Output 326 6.8 Loan Tables 331 6.9 Estimating the Execution Time 334 6.10 Recursive Methods (Optional) 338 6.11 Sample Development 343 7 Defining Your Own Classes—Part 2 365 7.1 Returning an Object from a Method 366 7.2 The Reserved Word this 370 7.3 Overloaded Methods and Constructors 378 7.4 Class Variables and Methods 383 wu23399_fm.qxd 1/10/07 11:53 Page vii
  • 9. viii Contents 7.5 Call-by-Value Parameter Passing 387 7.6 Organizing Classes into a Package 394 7.7 Using Javadoc Comments for Class Documentation 395 7.8 The Complete Fraction Class 400 7.9 Sample Development 410 8 Exceptions and Assertions 437 8.1 Catching Exceptions 438 8.2 Throwing Exceptions and Multiple catch Blocks 445 8.3 Propagating Exceptions 450 8.4 Types of Exceptions 458 8.5 Programmer-Defined Exceptions 461 8.6 Assertions 463 8.7 Sample Development 469 9 Characters and Strings 487 9.1 Characters 488 9.2 Strings 491 9.3 Pattern Matching and Regular Expression 502 9.4 The Pattern and Matcher Classes 509 9.5 Comparing Strings 513 9.6 StringBuffer and StringBuilder 515 9.7 Sample Development 521 10 Arrays and Collections 543 10.1 Array Basics 544 10.2 Arrays of Objects 555 10.3 The For-Each Loop 565 wu23399_fm.qxd 1/10/07 11:53 Page viii
  • 10. Contents ix 10.4 Passing Arrays to Methods 569 10.5 Two-Dimensional Arrays 576 10.6 Lists and Maps 583 10.7 Sample Development 596 11 Sorting and Searching 619 11.1 Searching 620 11.2 Sorting 624 11.3 Heapsort 632 11.4 Sample Development 645 12 File Input and Output 669 12.1 File and JFileChooser Objects 670 12.2 Low-Level File I/O 679 12.3 High-Level File I/O 684 12.4 Object I/O 693 12.5 Sample Development 700 13 Inheritance and Polymorphism 713 13.1 A Simple Example 714 13.2 Defining Classes with Inheritance 717 13.3 Using Classes Effectively with Polymorphism 721 13.4 Inheritance and Member Accessibility 724 13.5 Inheritance and Constructors 729 13.6 Abstract Superclasses and Abstract Methods 733 13.7 Inheritance versus Interface 738 13.8 Sample Development 739 wu23399_fm.qxd 1/12/07 13:15 Page ix
  • 11. x Contents 14 GUI and Event-Driven Programming 765 14.1 Simple GUI I/O with JOptionPane 768 14.2 Customizing Frame Windows 771 14.3 GUI Programming Basics 777 14.4 Text-Related GUI Components 787 14.5 Layout Managers 798 14.6 Effective Use of Nested Panels 808 14.7 Other GUI Components 817 14.8 Menus 835 14.9 Handling Mouse Events 839 15 Recursive Algorithms 859 15.1 Basic Elements of Recursion 860 15.2 Directory Listing 861 15.3 Anagram 863 15.4 Towers of Hanoi 866 15.5 Quicksort 868 15.6 When Not to Use Recursion 873 16 Memory Allocation Schemes and Linked Data Structures 879 16.1 Contiguous Memory Allocation Scheme 881 16.2 Noncontiguous Memory Allocation Scheme 886 16.3 Manipulating Linked Lists 890 16.4 Linked Lists of Objects 903 16.5 Sample Development 908 wu23399_fm.qxd 1/10/07 11:53 Page x
  • 12. Contents xi 17 Generics and Type Safety 945 17.1 Generic Classes 946 17.2 Generics and Collections 961 17.3 Generics,Inheritance,and Java Interface 969 17.4 Additional Topics and Pitfalls 974 18 List ADT 981 18.1 The List ADT 982 18.2 The List Interface 988 18.3 The Array Implementation of the List ADT 992 18.4 The Linked-List Implementation of the List ADT 1001 18.5 The Linked Implementation with the Head Node 1018 18.6 The Iterator Design Pattern 1022 18.7 Sample Development 1027 19 Stack ADT 1035 19.1 The Stack ADT 1036 19.2 The Stack Interface 1040 19.3 The Array Implementation 1042 19.4 The Linked-List Implementation 1047 19.5 Implementation Using NPSList 1052 19.6 Sample Applications:Matching HTML Tags 1053 19.7 Sample Applications:Solving a Maze with Backtracking 1060 wu23399_fm.qxd 1/10/07 11:53 Page xi
  • 13. xii Contents 20 Queue ADT 1069 20.1 The Queue ADT 1070 20.2 The Queue Interface 1073 20.3 The Array Implementation 1075 20.4 The Linked-List Implementation 1082 20.5 Implementation Using NPSList 1088 20.6 Priority Queue 1089 Appendix A 1099 Appendix B 1107 Appendix C 1133 Appendix D 1155 Index 1163 wu23399_fm.qxd 1/10/07 11:53 Page xii
  • 14. xiii P r e f a c e This book is an in-depth introduction to object-oriented programming using the Java programming language. In addition to covering traditional topics for a CS1 course, some of the more advanced topics such as recursion and linked lists are in- cluded to provide a comprehensive coverage of beginning to intermediate-level ma- terials. There are more materials in the book than what are normally covered in a typical CS1 course. An instructor may want to teach some of the chapters on data structures in an advanced CS1 course. Topics covered in Chapters 16 to 20 are also suitable for use in a CS2 course. Key Differences from the Standard Edition This comprehensive edition is based on An Introduction to Object-Oriented Pro- gramming with Java, Fourth Edition. The key differences between this comprehen- sive version and the fourth edition standard version are as follows: 1. Data Structures Chapters. Chapter 16 covers topics on managing linked nodes. Using this as the foundation, Chapters 18 through 20 present three ab- stract data types (ADTs) List, Stack, and Queue, respectively. For all three ADTs, both array-based and linked-list implementations are shown, and their relative advantages and disadvantages are discussed. 2. More Discussion on Java 5.0 Features. Many of the new Java 5.0 features are explained and used in the sample programs. They include the enumerator type, the for-each loop construct, auto boxing and unboxing, and the generics. One complete chapter (Chapter 17) is dedicated to the generics. 3. Exclusive Use of Console Input and Output. All the GUI related topics, including the JOptionPane class, are moved to Chapter 14. Sample programs before Chapter 14 use the standard console input (Scanner) and output (System.out). Those who want to use JOptionPane for simple input and output can do so easily by covering Section 14.1 before Chapter 3. wu23399_fm.qxd 1/10/07 11:53 Page xiii
  • 15. xiv Preface Book Organization There are 21 chapters in this book, numbered from 0 to 20. The first 11 chapters cover the core topics that provide the fundamentals of programming. Chapters 11 to 15 cover intermediate-level topics such as sorting, searching, recursion, inheritance, polymorphism, and file I/O. And Chapters 16 to 20 cover topics related to data structures. There are more than enough topics for one semester. After the first 11 chapters (Ch 0 to Ch 10), instructors can mix and match materials from Chapters 11 to 20 to suit their needs. We first show the dependency relationships among the chapters and then provide a brief summary of each chapter. Chapter Dependency For the most part, chapters should be read in sequence, but some variations are possible, especially with the optional chapters. Here’s a simplified dependency graph: 0 1 2 3 4 5 6 7 8 9 10 15 14* 13 12 11 18 19 20 17 16 *Note: Some examples use arrays, but the use of arrays is not an integral part of the examples. These examples can be modified to those that do not use arrays. Many topics from the early part of the chapter can be introduced as early as after Chapter 2. wu23399_fm.qxd 1/10/07 11:53 Page xiv
  • 16. Preface xv Brief Chapter Summary Here is a short description of each chapter: • Chapter 0 is an optional chapter. We provide background information on computers and programming languages. This chapter can be skipped or as- signed as an outside reading if you wish to start with object-oriented pro- gramming concepts. • Chapter 1 provides a conceptual foundation of object-oriented programming. We describe the key components of object-oriented programming and illus- trate each concept with a diagrammatic notation using UML. • Chapter 2 covers the basics of Java programming and the process of editing, compiling, and running a program. From the first sample program presented in this chapter, we emphasize object-orientation. We will introduce the standard classes String, Date, and SimpleDateFormat so we can reinforce the notion of object declaration, creation, and usage. Moreover, by using these standard classes, students can immediately start writing practical programs.We describe and illustrate console input with System.in and the new Scanner class and output with System.out. • Chapter 3 introduces variables, constants, and expressions for manipulating numerical data. We explain the standard Math class from java.lang and introduce more standard classes (GregorianCalendar and DecimalFormat) to continually reinforce the notion of object-orientation. We describe additional methods of the Scanner class to input numerical values. Random number generation is introduced in this chapter. The optional section explains how the numerical values are represented in memory space. • Chapter 4 teaches the basics of creating programmer-defined classes. We keep the chapter accessible by introducting only the fundamentals with illus- trative examples. The key topics covered in this chapter are constructors, vis- ibility modifiers (public and private), local variables, and passing data to methods. We provide easy-to-grasp illustrations that capture the essence of the topics so the students will have a clear understanding of them. • Chapter 5 explains the selection statements if and switch. We cover boolean expressions and nested-if statements. We explain how objects are compared by using equivalence (==) and equality (the equals and compareTo methods). We use the String and the programmer-defined Fraction classes to make the distinction between the equivalence and equality clear. Drawing 2-D graphics is introduced, and a screensaver sample development program is developed. We describe the new Java 5.0 feature called enumerated type in this chapter. • Chapter 6 explains the repetition statements while, do–while, and for. Pitfalls in writing repetition statements are explained. One of the pitfalls to avoid is the use of float or double for the data type of a counter variable. We illustrate this pitfall by showing a code that will result in infinite loop. Finding the great- est common divisor of two integers is used as an example of a nontrivial loop statement. We show the difference between the straightforward (brute-force) wu23399_fm.qxd 1/10/07 11:54 Page xv
  • 17. xvi Preface and the clever (Euclid’s) solutions. We introduce the Formatter class (new to Java 5.0) and show how the output can be aligned nicely. The optional last sec- tion of the chapter introduces recursion as another technique for repetition. The recursive version of a method that finds the greatest common divisor of two integers is given. • Chapter 7 is the second part of creating programmer-defined classes. We introduce new topics related to the creation of programmer-defined classes and also repeat some of the topics covered in Chapter 4 in more depth. The key topics covered in this chapter are method overloading, the reserved word this, class methods and variables, returning an object from a method, and pass-by-value parameter passing. As in Chapter 4, we provide many lucid illustrations to make these topics accessible to beginners. We use the Fraction class to illustrate many of these topics, such as the use of this and class methods. The complete definition of the Fraction class is presented in this chapter. • Chapter 8 teaches exception handling and assertions. The focus of this chap- ter is the construction of reliable programs. We provide a detailed coverage of exception handling in this chapter. We introduce an assertion and show how it can be used to improve the reliability of finished products by catching logical errors early in the development. • Chapter 9 covers nonnumerical data types: characters and strings. Both the String and StringBuffer classes are explained in the chapter. Another string class named StringBuilder (new to Java 5.) is briefly explained in this chapter. An important application of string processing is pattern matching. We describe pattern matching and regular expression in this chapter. We introduce the Pattern and Matcher classes and show how they are used in pattern matching. • Chapter 10 teaches arrays. We cover arrays of primitive data types and of ob- jects. An array is a reference data type in Java, and we show how arrays are passed to methods. We describe how to process two-dimensional arrays and explain that a two-dimensional array is really an array of arrays in Java. Lists and maps are introduced as a more general and flexible way to maintain a col- lection of data. The use of ArrayList and HashMap classes from the java.util package is shown in the sample programs. Also, we show how the WordList helper class used in Chapter 9 sample development program is implemented with another map class called TreeMap. • Chapter 11 presents searching and sorting algorithms. Both N2 and Nlog2N sorting algorithms are covered. The mathematical analysis of searching and sorting algorithms can be omitted depending on the students’ background. • Chapter 12 explains the file I/O. Standard classes such as File and JFile- Chooser are explained. We cover all types of file I/O, from a low-level byte I/O to a high-level object I/O. We show how the file I/O techniques are used to implement the helper classes—Dorm and FileManager—in Chapter 8 and 9 sample development programs. The use of the Scanner class for inputting data from a textfile is also illustrated in this chapter. wu23399_fm.qxd 1/10/07 11:54 Page xvi
  • 18. Preface xvii • Chapter 13 discusses inheritance and polymorphism and how to use them ef- fectively in program design. The effect of inheritance for member accessibil- ity and constructors is explained. We also explain the purpose of abstract classes and abstract methods. • Chapter 14 covers GUI and event-driven programming. Only the Swing- based GUI components are covered in this chapter. We show how to use the JOptionPane class for a very simple GUI-based input and output. GUI com- ponents introduced in this chapter include JButton, JLabel, ImageIcon, JTextField, JTextArea, and menu-related classes. We describe the effective use of nested panels and layout managers. Handling of mouse events is described and illustrated in the sample programs. Those who do not teach GUI can skip this chapter altogether. Those who teach GUI can introduce the beginning part of the chapter as early as after Chapter 2. • Chapter 15 covers recursion. Because we want to show the examples where the use of recursion really shines, we did not include any recursive algorithm (other than those used for explanation purposes) that really should be written nonrecursively. • Chapter 16 covers contiguous and noncontiguous memory allocation schemes and introduces the concept of linked lists. Ample examples are provided to illustrate the manipulation of linked lists of primitive data types and linked lists of objects. This chapter lays the necessary foundation for the students to learn different techniques for implementing the abstract data types covered in Chapters 18 through 20. • Chapter 17 covers new Java 5.0 generics in detail. The chapter describes how generic classes are defined and how the type safety is supported by generics. A concrete example of using generics is shown by defining a simple linked list with generic nodes. • Chapter 18 introduces the concept of abstract data types (ADT) and covers the List ADT. Key features of the List ADT are explained and two implemen- tations using an array and a linked list are shown. The iterator pattern to tra- verse the elements in the List ADT is introduced. • Chapter 19 covers the Stack ADT. Key features of the Stack ADT are ex- plained and two implementations using an array and a linked list are shown. Sample applications that use stacks are described. • Chapter 20 covers the Queue ADT. Key features of the Stack ADT are ex- plained and two implementations using an array and a linked list are shown. A special type of queue called a priority queue is also intoduced in this chapter. wu23399_fm.qxd 1/10/07 11:54 Page xvii
  • 19. xviii Preface Development Exercises give students an opportunity to practice incremental development. Hallmark Features of the Text Problem Solving Printing the Initials Now that we have acquired a basic understanding of Java application programs, let’s write a new application.We will go through the design,coding,and testing phases of the software life cycle to illustrate the development process. Since the program we develop here is very simple,we can write it without really going through the phases.However,it is extremely important for you to get into a habit of developing a program by following the software life cycle stages. Small programs can be developed in a haphazard manner, but not large programs.We will teach you the development process with small programs first, so you will be ready to use it to create large programs later. We will develop this program by using an incremental development technique, which will develop the program in small incremental steps. We start out with a bare- bones program and gradually build up the program by adding more and more code to it. At each incremental step, we design, code, and test the program before moving on to the next step. This methodical development of a program allows us to focus our at- tention on a single task at each step, and this reduces the chance of introducing errors into the program. Problem Statement We start our development with a problem statement. The problem statement for our sample programs will be short,ranging from a sentence to a paragraph,but the problem statement for complex and advanced applications may contain many pages. Here’s the problem statement for this sample development exercise: Write an application that asks for the user’s first, middle, and last names and replies with the user’s initials. Overall Plan Our first task is to map out the overall plan for development.We will identify classes nec- essary for the program and the steps we will follow to implement the program.We begin with the outline of program logic.For a simple program such as this one,it is kind of obvi- ous; but to practice the incremental development, let’s put down the outline of program flow explicitly.We can express the program flow as having three tasks: 1. Get the user’s first,middle,and last names. 2. Extract the initials to formulate the monogram. 3. Output the monogram. Having identified the three major tasks of the program, we will now identify the classes we can use to implement the three tasks. First, we need an object to handle the input. At this point, we have learned about only the Scanner class, so we will use it here. Second, we need an object to display the result. Again, we will use System.out, as it is the only one we know at this point for displaying a string value. For the string Sample Development 2.5 Sample Development program tasks Sample Development Programs Most chapters include a sample development section that describes the process of incremental development. Development Exercises For the following exercises, use the incremental development methodology to implement the program. For each exercise, identify the program tasks, create a design document with class descriptions, and draw the program diagram. Map out the development steps at the start. Present any design alternatives and justify your selection. Be sure to perform adequate testing at the end of each development step. 8. In the sample development, we developed the user module of the keyless entry system. For this exercise, implement the administrative module that allows the system administrator to add and delete Resident objects and modify information on existing Resident objects. The module will also allow the user to open a list from a file and save the list to a file. Is it proper to implement the administrative module by using one class? Wouldn’t it be a better design if we used multiple classes with each class doing a single, well-defined task? 9. Write an application that maintains the membership lists of five social clubs in a dormitory. The five social clubs are the Computer Science Club, Biology Club, Billiard Club, No Sleep Club, and Wine Tasting Club. Use the Dorm wu23399_fm.qxd 1/10/07 11:54 Page xviii
  • 20. Preface xix Object-Oriented Approach We take the object-first approach to teaching object-oriented programming with emphasis on proper object-oriented design.The concept of objects is clearly illustrated from the very first sample program. /* Chapter 2 Sample Program: Displaying a Window File: Ch2Sample1.java */ import javax.swing.*; class Ch2Sample1 { public static void main(String[] args) { JFrame myWindow; myWindow = new JFrame(); myWindow.setSize(300, 200); myWindow.setTitle(My First Java Program); myWindow.setVisible(true); } } Dorm Door Resident User module Dorm Resident A helper class provided to us A class we implement One or more classes we implement Administrative module Figure 8.8 Program diagrams for the user and administrative modules.Notice the same Dorm and Resident classes are used in both programs.User and administrative modules will include one or more classes (at least one is programmer-defined). Good practices on object- oriented design are discussed throughout the book and illustrated through numerous sample programs. wu23399_fm.qxd 1/10/07 11:55 Page xix
  • 21. xx Preface Illustrative Diagrams Illustrative diagrams are used to explain all key concepts of programming such as the difference between object declaration and creation,the distinction between the primitive data type and the reference data type,the call-by-value parameter passing,inheritance,and many others. Numerical Data Object number1 = 237; number2 = number1; int number1, number2; alan = new Professor(); turing = alan; Professor alan, turing; number2 number1 turing alan number2 number1 turing alan number1 = 237; int number1, number2; alan = new Professor(); Professor alan, turing; number2 = number1; turing = alan; :Professor :Professor number2 number1 turing alan number1 = 237; int number1, number2; alan = new Professor(); Professor alan, turing; number2 = number1; turing = alan; 237 237 237 Figure 3.3 An effect of assigning the content of one variable to another. Figure 18.2 Sample version 2 add operations on myList. Before After Before After “cat” “gnu” “ape” “dog” “bee” 0 1 2 3 4 “cat” 0 “ape” 1 “dog” 2 “bee” 3 add(1, “gnu”) throws index-out-of-bounds-exception add(5, “gnu”) myList “cat” 0 “ape” 1 “dog” 2 “bee” 3 myList myList myList “cat” 0 “ape” 1 “dog” 2 “bee” 3 No structural change to the list Lucid diagrams are used effectively to explain data structures and abstract data types. wu23399_fm.qxd 1/10/07 11:55 Page xx
  • 22. Preface xxi Student Pedagogy Always define a constructor and initialize data members fully in the constructor so an object will be created in a valid state. It is not necessary to create an object for every variable we use. Many novice pro- grammers often make this mistake.For example,we write Fraction f1, f2; f1 = new Fraction(24, 36); f2 = f1.simplify( ); We didn’t write Fraction f1, f2; f1 = new Fraction(24, 36); f2 = new Fraction(1, 1); //not necessary f2 = f1.simplify( ); because it is not necessary.The simplify method returns a Fraction object, and in the calling program, all we need is a name we can use to refer to this returned Fraction object.Don’t forget that the object name (variable) and the actual object instance are two separate things. We can turn our simulation program into a real one by replacing the Door class with a class that actually controls the door. Java provides a mechanism called Java Native Interface (JNI) which can be used to embed a link to a low- level device driver code, so calling the open method actually unlocks the door. 1. What will be displayed on the console window when the following code is executed and the user enters abc123 and 14? Scanner scanner = new Scanner(System.in); try { int num1 = scanner.nextInt(); System.out.println(Input 1 accepted); int num2 = scanner.nextInt(); System.out.println(Input 2 accepted); } catch (InputMismatchException e) { System.out.println(Invalid Entry); } List the catch blocks in the order of specialized to more general exception classes. At most one catch block is executed,and all other catch blocks are ignored. Design Guidelines provide tips on good program design. Things to Remember boxes provide tips for students to remember key concepts. Tips,Hints,and Pitfalls provide important points for which to watch out. You Might Want to Know boxes give students interesting bits of information. Quick Check exercises at the end of the sections allow students to test their comprehension of topics. wu23399_fm.qxd 1/10/07 11:55 Page xxi
  • 23. xxii Preface Supplements for Instructors and Students On-Line Learning Center is located at www.mhhe.com/wu For Instructors • Complete set of PowerPoints, including lecture notes and figures. • Complete solutions for the exercises • Example Bank—Additional examples, which are searchable by topic, are provided online in a “bank” for instructors. • Homework Manager/Test Bank—Conceptual review questions are stored in this electronic question bank and can be assigned as exam questions or home- work. • Online labs which accompany this text, can be used in a closed lab, open lab, or for assigned programming projects. wu23399_fm.qxd 1/10/07 11:55 Page xxii
  • 24. Preface xxiii For Students • Compiler How Tos provide tutorials on how to get up and running on the most popular compilers to aid students in using IDEs. • Interactive Quizzes allow students to test what they learn and get immediate feedback. • Source code for all example programs in the book. • Answers to quick check exercises. • Glossary of key terms. • Recent News links relevant to computer science. • AdditionalTopics such as more on swing and an introduction to data structures. Acknowledgments First, I would like to thank the following reviewers for their comments, suggestions, and encouragement. Wu Focus Group—Jackson Hole, WY Elizabeth Adams, James Madison University GianMario Besana, Depaul University Michael Buckley, State University of New York, Buffalo James Cross, Auburn University Priscilla Dodds, Georgia Perimeter College Christopher Eliot, University of Massachusetts-Amherst Joanne Houlahan, John Hopkins University Len Myers, California Polytechnic State University, San Luis Obispo Hal Perkins, University of Washington William Shea, Kansas State University Marge Skubic, University of Missouri, Columbia Bill Sverdlik, Eastern Michigan University Suzanne Westbrook, University of Arizona wu23399_fm.qxd 1/10/07 11:55 Page xxiii
  • 25. xxiv Preface Reviewers Ajith, Abraham, Oklahoma State University Elizabeth Adams, James Madison University David L. Atkins, University of Oregon GianMario Besana, DePaul University Robert P. Burton, Brigham Young University Michael Buckley, State University of New York, Buffalo Rama Chakrapani, Tennessee Technological University Teresa Cole, Boise State University James Cross, Auburn University Priscilla Dodds, Georgia Perimeter College Kossi Delali Edoh, Montclair State University Christopher Eliot, University of Massachusetts-Amherst Michael Floeser, Rochester Institute of Technology Joanne Houlahan, John Hopkins University Michael N. Huhns, University of South Carolina Eliot Jacobson, University of California, Santa Barbara Martin Kendall, Montgomery Community College Mike Litman, Western Illinois University Len Myers, California Polytechnic State University, San Luis Obispo Jun Ni, University of Iowa Robert Noonan, College of William and Mary Jason S. O’Neal, Mississippi College Hal Perkins, University of Washington Gerald Ross, Lane Community College William Shea, Kansas State University Jason John Schwarz, North Carolina State University Marge Skubic, University of Missouri, Columbia Bill Sverdlik, Eastern Michigan University Peter Stanchev, Kettering University Krishnaprasad Thirunarayan, Wright State University David Vineyard, Kettering University Suzanne Westbrook, University of Arizona Melissa Wiggins, Mississippi College Zhiguang Xu, Valdosta State University. The following reviewers have provided feedback on the chapters new to this comprehensive edition: Eric Matson, Wright State University Tim Margush, University of Akron Roxanne Canosa, Rochester Institute of Technology Ivan Bajic, San Diego State University Carolyn Miller, North Carolina State Sunil Prabhakar, Purdue University Weining Zhang, University of Texas, San Antonio wu23399_fm.qxd 1/10/07 11:56 Page xxiv
  • 26. Preface xxv Personal Story In September, 2001, I changed my name for personal reasons. Prof C. Thomas Wu is now Prof Thomas W. Otani. To maintain continuity and not to confuse peo- ple, we continue to publish the book under my former name. For those who care to find out a little about my personal history can do so by visiting my web- site (www.drcaffeine.com). wu23399_fm.qxd 1/10/07 11:56 Page xxv
  • 28. O b j e c t i v e s After you have read and studied this chapter,you should be able to 1 0 • State briefly a history of computers. • Name and describe five major components of the computer. • Convert binary numbers to decimal numbers and vice versa. • State the difference between the low-level and high-level programming languages. Introduction to Computers and Programming Languages wu23392_ch00.qxd 12/12/06 17:23 Page 1
  • 29. 2 Chapter 0 Introduction to Computers and Programming Languages efore we embark on our study of computer programming, we will present some background information on computers and programming languages in this optional chapter. We provide a brief history of computers from the early days to present and describe the components found in today’s computers. We also present a brief history of programming languages from low-level machine languages to today’s object- oriented languages. 0.1 A History of Computers Humans have evolved from a primitive to a highly advanced society by continually inventing tools. Stone tools, gunpowder, wheels, and other inventions have changed the lives of humans dramatically. In recent history, the computer is arguably the most important invention. In today’s highly advanced society, computers affect our lives 24 hours a day: class schedules are formulated by computers, student records are maintained by computers, exams are graded by computers, dorm security sys- tems are monitored by computers, and numerous other functions that affect us are controlled by computers. Although the first true computer was invented in the 1940s, the concept of a computer is actually more than 160 years old. Charles Babbage is credited with inventing a precursor to the modern computer. In 1823 he received a grant from the British government to build a mechanical device he called the Difference Engine, intended for computing and printing mathematical tables. The device was based on rotating wheels and was operated by a single crank. Unfortunately, the technology of the time was not advanced enough to build the device. He ran into difficulties and eventually abandoned the project. But an even more grandiose scheme was already with him. In fact, one of the reasons he gave up on the Difference Engine may have been to work on his new con- cept for a better machine. He called his new device the Analytical Engine. This device, too, was never built. His second device also was ahead of its time; the tech- nology did not yet exist to make the device a reality. Although never built, the Ana- lytical Engine was a remarkable achievement because its design was essentially based on the same fundamental principles of the modern computer. One principle that stands out was its programmability. With the Difference Engine, Babbage would have been able to compute only mathematical tables, but with the Analytical Engine he would have been able to compute any calculation by inputting instructions on punch cards. The method of inputting programs to computers on punch cards was actually adopted for real machines and was still in wide use as late as the 1970s. The Analytical Engine was never built, but a demonstration program was written by Ada Lovelace, a daughter of the poet Lord Byron. The programming lan- guage Ada was named in honor of Lady Lovelace, the first computer programmer. In the late 1930s John Atanasoff of Iowa State University, with his graduate student Clifford Berry, built the prototype of the first automatic electronic calculator. I n t r o d u c t i o n B Charles Babbage Difference Engine Analytical Engine Ada Lovelace wu23392_ch00.qxd 12/12/06 17:23 Page 2
  • 30. 0.1 A History of Computers 3 One innovation of their machine was the use of binary numbers. (We discuss binary numbers in Sec. 0.2.)At around the same time, HowardAiken of Harvard University was working on the Automatic Sequence-Controlled Calculator, known more com- monly as MARK I, with support from IBM and the U.S. Navy. MARK I was very similar to the Analytical Engine in design and was described as “Babbage’s dream come true.” MARK I was an electromechanical computer based on relays. Mechanical relays were not fast enough, and MARK I was quickly replaced by machines based on electronic vacuum tubes. The first completely electronic computer, ENIAC I (Electronic Numerical Integrator And Calculator), was built at the University of Pennsylvania under the supervision of John W. Mauchly and J. Presper Eckert. Their work was influenced by the work of John Atanasoff. ENIAC I was programmed laboriously by plugging wires into a control panel that resembled an old telephone switchboard. Programming took an enor- mous amount of the engineers’ time, and even making a simple change to a pro- gram was a time-consuming effort. While programming activities were going on, the expensive computer sat idle. To improve its productivity, John von Neumann of Princeton University proposed storing programs in the computer’s memory. This stored program scheme not only improved computation speed but also al- lowed far more flexible ways of writing programs. For example, because a pro- gram is stored in the memory, the computer can change the program instructions to alter the sequence of the execution, thereby making it possible to get different results from a single program. We characterized these early computers with vacuum tubes as first-generation computers. Second-generation computers, with transistors replacing the vacuum tubes, started appearing in the late 1950s. Improvements in memory devices also increased processing speed further. In the early 1960s, transistors were replaced by integrated circuits, and third-generation computers emerged. A single integrated circuit of this period incorporated hundreds of transistors and made the construction of minicomputers possible. Minicomputers are small enough to be placed on desk- tops in individual offices and labs. The early computers, on the other hand, were so huge that they easily occupied the whole basement of a large building. Advancement of integrated circuits was phenomenal. Large-scale integrated circuits, commonly known as computer chips or silicon chips, packed the power equivalent to thousands of transistors and made the notion of a “computer on a sin- gle chip” a reality. With large-scale integrated circuits, microcomputers emerged in the mid-1970s. The machines we call personal computers today are descendants of the microcomputers of the 1970s. The computer chips used in today’s personal computers pack the power equivalent to several millions of transistors. Personal computers are fourth-generation computers. Early microcomputers were isolated, stand-alone machines. The word per- sonal describes a machine as a personal device intended to be used by an individual. However, it did not take long to realize there was a need to share computer resources. For example, early microcomputers required a dedicated printer. Wouldn’t it make more sense to have many computers share a single printer? Wouldn’t it also make sense to share data among computers, instead of duplicating the same data on MARK I ENIAC I stored program generations of computers wu23392_ch00.qxd 12/12/06 17:23 Page 3
  • 31. individual machines? Wouldn’t it be nice to send electronic messages between the computers? The notion of networked computers arose to meet these needs. Computers of all kinds are connected into a network. A network that connects computers in a single building or in several nearby buildings is called a local-area network or LAN. A network that connects geographically dispersed computers is called a wide-area network or WAN. These individual networks can be connected further to form interconnected networks called internets. The most famous internet is simply called the Internet. The Internet makes the sharing of worldwide informa- tion possible and easy. The hottest tool for viewing information on the Internet is a Web browser. A Web browser allows you to experience multimedia information consisting of text, audio, video, and other types of information. We will describe how Java is related to the Internet and Web browsers in Section 0.4. 4 Chapter 0 Introduction to Computers and Programming Languages network LAN WAN internet 1. Who was the first computer programmer? 2. Who designed the Difference Engine and Analytical Engine? 3. How many generations of computers are there? 0.2 Computer Architecture A typical computer today has five basic components: RAM, CPU, storage devices, I/O (input/output) devices, and communication devices. Figure 0.1 illustrates these five components. Before we describe the components of a computer, we will explain the binary numbering system used in a computer. Binary Numbers To understand the binary number system, let’s first review the decimal number sys- tem in which we use 10 digits: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. To represent a number in the decimal system, we use a sequence of one or more of these digits. The value that each digit in the sequence represents depends on its position. For example, consider the numbers 234 and 324. The digit 2 in the first number represents 200, whereas the digit 2 in the second number represents 20. A position in a sequence has a value that is an integral power of 10. The following diagram illustrates how the If you want to learn more about the history of computing,there is a wealth of information available on the Web.You can start your exploration from www.yahoo.com/Computers_and_Internet/History For more information on the pioneers of computers,visit en.wikipedia.org/wiki/category:Computer_pioneers wu23392_ch00.qxd 12/12/06 17:23 Page 4
  • 32. 0.2 Computer Architecture 5 values of positions are determined: The value of a decimal number (represented as a sequence of digits) is the sum of the digits, multiplied by their position values, as illustrated: 2 102 4 101 8 100 7 101 2 100 4 10 8 1 7 110 200 40 8 710 248.7 2 102 4 101 8 100 • 7 101 104 103 102 101 100 Decimal Point Position Values 101 102 • • • • • • 103 • Figure 0.1 A simplified view of an architecture for a typical computer. Output Devices Communication Devices Input Devices RAM CPU Storage Devices Printer (output device) Monitor (output device) Main Unit (housing CPU, RAM, storage devices, and communication devices) Mouse (input device) Keyboard (input device) wu23392_ch00.qxd 12/12/06 17:23 Page 5
  • 33. In the decimal number system, we have 10 symbols, and the position values are integral powers of 10. We say that 10 is the base or radix of the decimal number system. The binary number system works the same as the decimal number system but uses 2 as its base. The binary number system has two digits (0 and 1) called bits, and position values are integral powers of 2. The following diagram illustrates how the values of positions are determined in the binary system: The value of a binary number (represented as a sequence of bits) is the sum of the bits, multiplied by their position values, as illustrated: 1 22 0 21 1 20 1 21 1 4 0 2 1 1 1 12 4 0 1 12 5.5 So the binary number 101.1 is numerically equivalent to the decimal num- ber 5.5. This illustration shows how to convert a given binary number to the decimal equivalent. How about converting a given decimal number to its binary equivalent? The following steps show how to convert a decimal number (only the whole numbers) to the equivalent binary number. The basic idea goes something like this: 1. Divide the number by 2. 2. The remainder is the bit value of the 20 position. 3. Divide the quotient by 2. 4. The remainder is the bit value of the 21 position. 5. Divide the quotient by 2. 6. The remainder is the bit value of the 22 position. 7. Repeat the procedure until you cannot divide any further, that is, until the quotient becomes 0. 1 22 0 21 1 20 • 1 21 24 23 • • • • • • 22 21 20 Binary Point Position Values • 21 22 23 6 Chapter 0 Introduction to Computers and Programming Languages base-2 numbers binary number bits binary-to- decimal conversion decimal-to- binary conversion wu23392_ch00.qxd 12/12/06 17:23 Page 6
  • 34. 0.2 Computer Architecture 7 When you pay closer attention to the on/off switch on computers and other electronic devices,you should notice an icon like this This is a stylized representation of binary digits 0 and 1. RAM Random access memory or RAM is a repository for both program instructions and data manipulated by the program during execution. RAM is divided into cells, with each cell having a unique address. Typically, each cell consists of 4 bytes (B), and a single byte (1 B) in turn consists of 8 bits. Each bit, which can be either on or off, represents a single binary digit. RAM is measured by the number of bytes it contains. For example, 128 kilobytes (KB) of RAM contains 128 1024 131,072 B because 1 KB is equal to 210 1024 B. Notice that 1 K is not equal to 103 , although 103 1000 is a close approximation to 210 1024. The first IBM PC introduced in 1981 came with 16 KB of RAM, and the first Macintosh com- puter introduced in 1984 came with 128 KB of RAM. In contrast, a typical PC today has anywhere from 128 MB to 512 MB of RAM. Given that 1 MB is equal to 1024 KB, we know that 256 MB means 256 1024 KB 262,144 KB 262,144 1024 B 268,435,456 B. RAM byte The following diagram illustrates the conversion of decimal number 25. Division Division Division Division Division #5 #4 #3 #2 #1 24 23 22 21 20 16 8 0 0 1 25 The binary system is more suitable for computers than the decimal system be- cause it is far easier to design an electrical device that can distinguish two states (bits 0 and 1) than 10 states (digits 0 through 9). For example, we can represent 1 by turning the switch on and 0 by turning the switch off. In a real computer, 0 is repre- sented by electrical voltage below a certain level and 1 by electrical voltage at or above this level. 12 21 2 5 1 24 1 6 21 1 2 1 12 0 3 21 6 1 6 0 1 21 3 1 2 1 0 21 1 1 0 1 wu23392_ch00.qxd 12/12/06 17:23 Page 7
  • 35. CPU The central processing unit or CPU is the brain of a computer. The CPU is the com- ponent that executes program instructions by fetching an instruction (stored in RAM), executing it, fetching the next instruction, executing it, and so on until it en- counters an instruction to stop. The CPU contains a small number of registers, which are high-speed devices for storing data or instructions temporarily. The CPU also contains the arithmetic-logic unit (ALU), which performs arithmetic operations such as addition and subtraction and logical operations such as comparing two numbers. CPUs are characterized by their clock speeds. For example, in the Intel Pentium 200, the CPU has a clock speed of 200 megahertz (MHz). The hertz is a unit of frequency equal to 1 cycle per second. A cycle is a period of time between two on states or off states. So 200 MHz equals 200,000,000 cycles per second. The fastest CPU for commercially available personal computers was around 200 MHz in 1997 when the first edition of this textbook was published. But by the beginning of 1998, many vendors started selling 300-MHz machines. And in a mere 6 months, by the middle of 1998, the top-of-the-line personal computers were 400-MHz ma- chines. As of this writing in late 2002, we see computers with 2.0-GHz (2000-MHz) CPU being advertised and sold. The increase of the CPU speed in the last two decades is truly astonishing. The clock speed of the Intel 8080, the CPU introduced in 1974 that started the PC revolution, was a mere 2 MHz. In contrast, the clock speed of the Intel Pentium 4 introduced in 2001 was 2 GHz (2000 MHz). Table 0.1 lists some of the Intel processors. I/O Devices Input/output or I/O devices allow communication between the user and the CPU. Input devices such as keyboards and mice are used to enter data, programs, and commands in the CPU. Output devices such as monitors and printers are used to display or print information. Other I/O devices include scanners, bar code readers, magnetic strip readers, digital video cameras, and musical instrument digital inter- face (MIDI) devices. Storage Devices Storage devices such as disk and tape drives are used to store data and programs. Sec- ondary storage devices are called nonvolatile memory, while RAM is called volatile memory. Volatile means the data stored in a device will be lost when the power to the device is turned off. Being nonvolatile and much cheaper than RAM, secondary stor- age is an ideal medium for permanent storage of large volumes of data. A secondary storage device cannot replace RAM, though, because secondary storage is far slower in data access (getting data out and writing data in) compared to RAM. The most common storage device today for personal computers is a disk drive. There are two kinds of disks: hard and floppy (also known as diskettes). Hard disks provide much faster performance and larger capacity, but are normally not re- movable; that is, a single hard disk is permanently attached to a disk drive. Floppy disks, on the other hand, are removable, but their performance is far slower and their capacity far smaller than those of hard disks. As the standard floppy disks can 8 Chapter 0 Introduction to Computers and Programming Languages CPU register clock speed I/O devices nonvolatile and volatile memory wu23392_ch00.qxd 12/12/06 17:23 Page 8
  • 36. store only up to approximately 1.44 MB, they are becoming less useful in today’s world of multimegabyte image and sound files. They are fast becoming obsolete, and hardly anybody uses them anymore. Removable storage media with much higher capacity such as zip disks (capable of holding 100 to 250 MB of data) re- placed floppy disks in late 1990s. Computer technology moves so quickly that zip disks themselves are already becoming obsolete. The most common form of portable storage medium today (2006) is a compact USB flash drive, whose capac- ity ranges from 125 MB to 16 GB. Hard disks can store a huge amount of data, typically ranging from 20 GB (gigabyte; 1 GB 1024 MB) to 80 GB for a standard desktop PC in 2002. Portable and removable hard disk drives, with performance and capacity that rival those of nonremovable hard disks, are also available, but their use is not widespread. Compact disks (CDs) are very popular today for storing massive amounts of data, approximately 700 MB. Many software packages we buy today—computer 0.2 Computer Architecture 9 Table Table 0.1 A table of Intel processors.For some CPUs,several types with different clock speeds are possible.In such case,only the fastest clock speed is shown.For more information on Intel CPUs,visit http://www.intel.com. Date Clock Speed CPU Introduced (MHz) 4004 11/15/71 0.108 8008 4/1/72 0.200 1970s 8080 4/1/74 2 8088 6/1/79 8 80286 2/1/82 12 1980s 80386SX 6/16/88 16 80486DX 4/10/89 25 Pentium 3/22/93 66 Pentium Pro 11/1/95 200 1990s Pentium II 5/7/97 300 Pentium II Xeon 6/29/98 400 Pentium III 10/25/99 733 Xeon 9/25/01 2000 2000s Pentium 4 4/27/01 2000 Itanium 2 7/8/02 1000 Pentium 4 Extreme 2/2/04 3400 Edition Core 2 Extreme 7/27/06 3200 wu23392_ch00.qxd 12/12/06 17:23 Page 9
  • 37. games, word processors, and others—come with a single CD. Before the CD became apopularstoragedeviceforcomputers,somesoftwarecamewithmorethan20floppy diskettes. Because of the massive storage capacity of the CD, most computer vendors eliminated printed manuals altogether by putting the manuals on the CD. 10 Chapter 0 Introduction to Computers and Programming Languages Communication Devices A communication device connects the personal computer to an internet. The most common communication device for computers at home and in small offices is the modem. A modem, which stands for modulator-demodulator, is a device that con- verts analog signals to digital and digital signals to analog. By using a modem, a computer can send to and receive data from another computer over the phone line. The most critical characteristic of a modem is its transmission speed, which is mea- sured in bits per second (bps). A typical speed for a modem is 56,000 bps, com- monly called a 56K modem. Under an ideal condition (no line noise or congestion), a 56K modem can transfer a 1 MB file in about 21 ⁄2 minutes. Frequently, though, the actual transfer rate is much lower than the possible maximum. So-called DSL and cable modems are not truly modems because they transfer data strictly in digital mode, which allows for much faster connection speeds of 144K or above. High- speed satellite connection to the Internet is also available today. A communication device for connecting a computer to a LAN is a network interface card (NIC). A NIC can transfer data at a much faster rate than the fastest modem. For instance, a type of NIC called 10BaseT can transfer data at the rate of 10 Mbps over the network. Traditional networks are connected, or wired, by the cables. Increasingly, networks are connected wirelessly, where data are carried over radio waves. Wireless networking is called WiFi or 802.11 networking. Today you will find wireless networking almost universally available at airports and hotels. communication device 1. Name five major components of a computer. 2. What is the difference between volatile and nonvolatile memory? 3. What does the acronym CPU stand for? 4. How many bytes does the 64 KB RAM have? 5. Which device connects a computer to the Internet using a phone line? Today we see more and more companies are even eliminating CDs and promoting “boxless”online distribution of software.With this scheme,we go to their websites and download the software,after paying for it with our credit card.Maybe someday we may be able to buy textbooks in the same manner and stop carrying 20 lb of dead trees in our backpacks. wu23392_ch00.qxd 12/12/06 17:23 Page 10
  • 38. 0.3 Programming Languages Programming languages are broadly classified into three levels: machine languages, assembly languages, and high-level languages. Machine language is the only pro- gramming language the CPU understands. Each type of CPU has its own machine language. For example, the Intel Pentium and Motorola PowerPC understand differ- ent machine languages. Machine-language instructions are binary-coded and very low level—one machine instruction may transfer the contents of one memory loca- tion into a CPU register or add numbers in two registers. Thus we must provide many machine-language instructions to accomplish a simple task such as finding the aver- age of 20 numbers.Aprogram written in machine language might look like this: 10110011 00011001 01111010 11010001 10010100 10011111 00011001 01011100 11010001 10010000 10111011 11010001 10010110 One level above machine language is assembly language, which allows “higher-level” symbolic programming. Instead of writing programs as a sequence of bits, assembly language allows programmers to write programs by using sym- bolic operation codes. For example, instead of 10110011, we use MV to move the contents of a memory cell into a register. We also can use symbolic, or mnemonic, names for registers and memory cells. A program written in assembly language might look like this: MV 0, SUM MV NUM, AC ADD SUM, AC STO SUM, TOT Since programs written in assembly language are not recognized by the CPU, we use an assembler to translate programs written in assembly language into machine-language equivalents. Compared to writing programs in machine lan- guage, writing programs in assembly language is much faster, but not fast enough for writing complex programs. High-level languages were developed to enable programmers to write pro- grams faster than when using assembly languages. For example, FORTRAN (FORmula TRANslator), a programming language intended for mathematical com- putation, allows programmers to express numerical equations directly as X = (Y + Z) / 2 COBOL (COmmon Business-Oriented Language) is a programming language in- tended for business data processing applications. FORTRAN and COBOL were de- veloped in the late 1950s and early 1960s and are still in use. BASIC (Beginners All-purpose Symbolic Instructional Code) was developed specifically as an easy language for students to learn and use. BASIC was the first high-level language 0.3 Programming Languages 11 machine language machine code assembly code assembler high-level code high-level languages assembly language wu23392_ch00.qxd 12/12/06 17:23 Page 11
  • 39. available for microcomputers. Another famous high-level language is Pascal, which was designed as an academic language. Since programs written in a high-level lan- guage are not recognized by the CPU, we must use a compiler to translate them to assembly language equivalents. The programming language C was developed in the early 1970s at ATT Bell Labs. The C++ programming language was developed as a successor of C in the early 1980s to add support for object-oriented programming. Object-oriented pro- gramming is a style of programming gaining wider acceptance today. Although the concept of object-oriented programming is old (the first object-oriented program- ming language, Simula, was developed in the late 1960s), its significance wasn’t realized until the early 1980s. Smalltalk, developed at Xerox PARC, is another well-known object-oriented programming language. The programming language we use in this book is Java, the newest object-oriented programming language, developed at Sun Microsystems. 0.4 Java Java isanewobject-orientedlanguagethatisreceivingwideattentionfrombothindus- try and academia. Java was developed by James Gosling and his team at Sun Microsys- tems in California. The language was based on C and C++ and was originally intended for writing programs that control consumer appliances such as toasters, microwave ovens,andothers.ThelanguagewasfirstcalledOak,namedaftertheoaktreeoutsideof Gosling’s office, but the name was already taken, so the team renamed it Java. Java is often described as a Web programming language because of its use in writing programs called applets that run within a Web browser. That is, you need a Web browser to execute Java applets. Applets allow more dynamic and flexible dis- semination of information on the Internet, and this feature alone makes Java an at- tractive language to learn. However, we are not limited to writing applets in Java. We can write Java applications also. A Java application is a complete stand-alone program that does not require a Web browser. A Java application is analogous to a program we write in other programming languages. In this book, we focus on Java applications because our objective is to teach the fundamentals of object-oriented programming that are applicable to all object-oriented programming languages. We chose Java for this textbook mainly for its clean design. The language de- signers of Java took a minimalist approach; they included only features that are in- dispensable and eliminated features that they considered excessive or redundant. This minimalist approach makes Java a much easier language to learn than other object-oriented programming languages. Java is an ideal vehicle for teaching the fundamentals of object-oriented programming. 12 Chapter 0 Introduction to Computers and Programming Languages compiler Java applet application • Charles Babbage invented the Difference Engine and Analytical Engine, precursors to the modern computer. • Ada Lovelace is considered the first computer programmer. • The first two modern computers were MARK I and ENIAC I. S u m m a r y wu23392_ch00.qxd 12/12/06 17:23 Page 12
  • 40. • John von Neumann invented the stored-program approach of executing programs. • Computers are connected into a network. Interconnected networks are called internets. • Binary numbers are used in computers. • A typical computer consists of five components: RAM, CPU, storage devices, I/O devices, and communication devices. • There are three levels of programming languages: machine, assembly, and high-level. • Java is one of the newest high-level programming languages in use today. This textbook teaches how to program using Java. Exercises 13 K e y C o n c e p t s network LAN WAN internets and Internet CPU RAM I/O devices communication devices binary numbers binary-to-decimal conversion machine language assembly language assembler high-level language compiler Java E x e r c i s e s 1. Visit your school’s computer lab or a computer store, and identify the different components of the computers you see. Do you notice any unique input or output devices? 2. Visit your school’s computer lab and find out the CPU speed, RAM size, and hard disk capacity of its computers. 3. Convert these binary numbers to decimal numbers. a. 1010 b. 110011 c. 110.01 d. 111111 4. Convert these decimal numbers to binary numbers. a. 35 b. 125 c. 567 d. 98 wu23392_ch00.qxd 12/12/06 17:23 Page 13
  • 41. 5. What is the maximum decimal number you can represent in 4 bits? 16 bits? N bits? 6. If a computer has 128 MB of RAM, how many bytes are there? 7. How do high-level programming languages differ from low-level programming languages? 8. Consider a hypothetical programming language called Kona. Using Kona, you can write a program to compute and print out the sum of 20 integers entered by the user: let sum = 0; repeat 20 times [ let X = next input; add X to sum; ] printout sum; Is Kona a high-level language? Why or why not? 14 Chapter 0 Introduction to Computers and Programming Languages wu23392_ch00.qxd 12/12/06 17:23 Page 14
  • 42. Introduction to Object-Oriented Programming and Software Development O b j e c t i v e s After you have read and studied this chapter,you should be able to • Name the basic components of object- oriented programming. • Differentiate classes and objects. • Differentiate class and instance methods. • Differentiate class and instance data values. • Draw program diagrams using icons for classes,objects,and other components of object-oriented programming. • Describe the significance of inheritance in object-oriented programs. • Name and explain the stages of the software life cycle. 15 1 wu23399_ch01.qxd 12/12/06 17:24 Page 15
  • 43. efore we begin to write actual programs, we need to introduce a few basic concepts of object-oriented programming (OOP), the style of programming we teach in this book. The purpose of this chapter is to give you a feel for object-oriented program- ming and to introduce a conceptual foundation of object-oriented programming.You may want to refer to this chapter as you progress through the book. What we discuss in the next four sections is independent of any particular programming language. 16 Chapter 1 Introduction to Object-Oriented Programming and Software Development I n t r o d u c t i o n B object- oriented programming object Those of you who have some experience in programming, whether object- oriented or non-object-oriented,will probably find many similarities between Java and the programming languages you already know.This similarity may accelerate your learning process, but in many cases what seems to be similar at first may turn out to be quite different. So please do not jump to any conclusions about similarity prematurely. Another purpose of this chapter is to introduce the software development process. To be able to write programs, knowledge of the components of object- oriented programs is not enough. We must learn the process of developing pro- grams. We will present a brief introduction to the software development process in this chapter. 1.1 Classes and Objects The two most important concepts in object-oriented programming are the class and the object. In the broadest term, an object is a thing, both tangible and intangi- ble, that we can imagine. A program written in object-oriented style will consist of interacting objects. For a program to keep track of student residents of a college dormitory, we may have many Student, Room, and Floor objects. For another pro- gram to keep track of customers and inventory for a bicycle shop, we may have Customer, Bicycle, and many other types of objects. An object is comprised of data and operations that manipulate these data. For example, a Student object may con- sist of data such as name, gender, birth date, home address, phone number, and age and operations for assigning and changing these data values. We will use the nota- tion shown in Figure 1.1 throughout the book to represent an object. The notation we used in the book is based on the industry standard notation called UML, which stands for Unified Modeling Language. In some of the illustrations, we relax the rules of UML slightly for pedagogy. Almost all nontrivial programs will have many objects of the same type. For example, in the bicycle shop program we expect to see many Bicycle and other objects. Figure 1.2 shows two Bicycle objects with the names Moto-1 and Moto-2 and one Customer object with the name Jon Java. wu23399_ch01.qxd 12/12/06 17:24 Page 16
  • 44. Inside a program we write instructions to create objects. For the computer to be able to create an object, we must provide a definition, called a class. A class is a kind of mold or template that dictates what objects can and cannot do. An object is called an instance of a class. An object is an instance of exactly one class. An instance of a class belongs to the class. The two Bicycle objects Moto-1 and Moto-2 are instances of the Bicycle class. Once a class is defined, we can create as many instances of the class as a program requires. 1.1 Classes and Objects 17 class instance Object name We use a rectangle to represent an object and place the underlined name of the object inside the rectangle. Example: account1 This is an object named account1. Figure 1.1 A graphical representation of an object. Jon Java : Customer Moto-2 : Bicycle Moto-1 : Bicycle An object name is followed by the class name. Figure 1.2 Two Bicycle objects with the names Moto-1 and Moto-2 and one Customer object with the name Jon Java. A class must be defined before you can create an instance (object) of the class. Figure 1.3 shows a diagram that we will use throughout the book to represent a class. 1. Draw an object diagram for a Person class and two Person objects, Ms. Latte and Mr. Espresso. 2. What must be defined before you can create an object? wu23399_ch01.qxd 12/12/06 17:24 Page 17
  • 45. 1.2 Messages and Methods In writing object-oriented programs we must first define classes, and while the pro- gram is running, we use the classes and objects from these classes to accomplish tasks. A task can range from adding two numbers, to computing an interest payment for a college loan, to calculating the reentry angle of a space shuttle. To instruct a class or an object to perform a task, we send a message to it. For example, we send a message deposit to an Account object to deposit $100. For a class or an object to process the message, it must be programmed ac- cordingly. You cannot just send a message to any class or object. You can send a message only to the classes and objects that understand the message you send. For a class or an object to process the message it receives, it must possess a matching method, which is a sequence of instructions that a class or an object follows to perform a task. A method defined for a class is called a class method, and a method defined for an object is an instance method. 18 Chapter 1 Introduction to Object-Oriented Programming and Software Development Example: We use a rectangle to represent a class with its name appearing inside the rectangle. Class Name Notice the name of a class is not underlined while the name of an object is. Account Figure 1.3 A graphical representation of a class. Many beginning programmers may not see the distinction between the class and object as clearly as the more experienced programmers do.It may be helpful to compare the class and object to a woodcut and the prints produced from the woodcut.A woodcut is a block of wood engraved with a design for printing.Once you have a woodcut,you can make as many prints as you wish.Similarly,once you have a class,you can make as many objects from the class.Also,just as you cannot make prints without having a woodcut,you cannot create an object without first defining a class.For sample prints by the 19th-century Japanese artist Hiroshige,visit http://www.ibiblio.org/wm/paint/auth/hiroshige/ Another helpful analogy is a robot factory.A factory is a class,and the robots produced from the factory are the objects of the class.To create robots (instance),we need the factory (class) first.Those interested in mobile robots can visit http://www.ai.mit.edu/projects/mobile-robots/robots.html message method class and instance methods wu23399_ch01.qxd 12/12/06 17:24 Page 18
  • 46. Let’s look at an example of an instance method first. Suppose a method called walk is defined for a Robot object and instructs the robot to walk a designated dis- tance. With this method defined, we can send the message walk to a Robot object, along with the distance to be walked. A value we pass to an object is called an argument of a message. Notice that the name of the message we send to an object or a class must be the same as the method’s name. In Figure 1.4 we represent the send- ing of a message. The diagram in Figure 1.4 illustrates one-way communication; that is, an object carries out the requested operation (it walks the designated distance) but does not respond to the message sender. In many situations we need a reply in which an object responds by returning a value to the message sender. For exam- ple, suppose we want to know the distance from a robot to its nearest obstacle. The designer of a robot may include a method getObstacleDistance that returns the desired value. The diagram in Figure 1.5 shows a method that returns a value to the message sender. Instead of returning a numerical value, a method can re- port back the status of the requested operation. For example, a method walk can be defined to return the status success/fail to indicate whether the specified distance was covered successfully or not (e.g., it fails when the robot bumps into an obstacle). Now let’s look at an example of class methods. The class method getMaxi- mumSpeed shown in Figure 1.6 returns the maximum possible speed of all Robot objects. A method such as getMaximumSpeed that deals with collective information about the instances of a class is usually defined as a class method. So we define an instance method for a task that pertains to an individual instance and a class method for a task that pertains to all instances. 1.2 Messages and Methods 19 argument walk(25) fido : Robot Message walk with the argument 25. Figure 1.4 Sending the message walk to a Robot object. getObstacleDistance( ) distance fido : Robot This shows that we are not sending any argument. This shows the value distance is returned as a response to the message. Figure 1.5 The result distance is returned to the sender of the message. wu23399_ch01.qxd 12/12/06 17:24 Page 19
  • 47. 20 Chapter 1 Introduction to Object-Oriented Programming and Software Development getMaximumSpeed( ) maximum speed Robot Figure 1.6 The maximum possible speed of all Robot objects is returned by the class method getMaximumSpeed. 1. Draw an object diagram of an Account object with instance methods deposit and withdraw. 2. Is the getObstacleDistance method an instance or a class method? 1.3 Class and Instance Data Values Suppose the method deposit of an Account object instructs the object to add a given amount to the current balance. Where does the object keep the current balance? Remember that an object is comprised of data values and methods. Analogous to defining class and instance methods, we can define class and instance data values. For example, we define an instance data value current balance for Account objects to record the current balance. Figure 1.7 shows three Account objects with their data values current balance. Notice that they all have the same data value current balance.All instances of the same class will possess the same set of data values. The actual dollar amounts for current balance, as the diagram illustrates, differ from one instance to another. Items such as opening balance and account number are other possible instance data values for Account objects. A class data value is used to represent information shared by all instances or to represent collective information about the instances. For example, if every account must maintain a minimum balance of, say, $100, we can define a class data value minimum balance. An instance can access the class data values of the class to which it belongs, so every Account object can access the class data value minimum balance. Jill’s : Account 1304.98 current balance Jack’s : Account 354.00 current balance John’s : Account 908.55 current balance Figure 1.7 Three Account objects possess the same data value current balance, but the actual dollar amounts differ. instance data value class data value wu23399_ch01.qxd 12/12/06 17:24 Page 20
  • 48. Figure 1.8 shows how we represent a class data value. Notice that we underline the class data value. Because the objects of a class are underlined, and the class data val- ues are accessible to all objects of the class, we likewise underline the class data value to show this relationship. Data values are also called data members because they belong to a class or instance of the class. To appreciate the significance of a class data value, let’s see what happens if we represent minimum balance as an instance data value. Figure 1.9 shows three Account objects having different dollar amounts for the current balance but the same dollar amount for the minimum balance. Obviously, this duplication of mini- mum balance is redundant and wastes space. Consider, for example, what happens if the bank raises the minimum balance to $200. If there are 100 Account objects, then all 100 copies of minimum balance must be updated. We can avoid this by defining minimum balance as a class data value. Figure 1.10 shows another exam- ple where the opening and closing times are shared by all cafeterias on campus. There are two types of data values: those that can change over time and those that cannot. A data value that can change is called a variable, and one that cannot 1.3 Class and Instance Data Values 21 John’s : Account 908.55 current balance Jill’s : Account 1304.98 current balance Account minimum balance 100.00 Jack’s : Account 354.00 current balance Notice the class data value is underlined to show the fact that this value is accessible to individual objects, which are underlined. Figure1.8 Three Account objects sharing information (minimumbalance $100) stored as a class data value. data member variable John’s : Account 908.55 current balance minimum balance 100.00 Jill’s : Account 1304.98 current balance minimum balance 100.00 Jack’s : Account 354.00 current balance minimum balance 100.00 Figure1.9 ThreeAccountobjectsduplicatinginformation(minimumbalance $100)ininstancedatavalues. wu23399_ch01.qxd 12/12/06 17:24 Page 21
  • 49. change is a constant. Figure 1.11 illustrates how we represent and distinguish be- tween variables and constants. We use the keyword frozen for constants to indicate that they cannot change. Notice that we now have four kinds of data values: class variables, class constants, instance variables, and instance constants. 22 Chapter 1 Introduction to Object-Oriented Programming and Software Development Union : Cafeteria 1917.34 revenue West : Cafeteria 2306.99 revenue QuikBite : Cafeteria 430.75 revenue Cafeteria opens 0600 closes 2100 Figure 1.10 Three Cafeteria objects sharing the same opening and closing times,stored as class data values. John’s : Account 908.55 current balance opening balance {frozen} 246.00 Jill’s : Account 1304.98 current balance opening balance {frozen} 50.00 Account 100.00 minimum balance account prefix {frozen} 6427 Jack’s : Account 354.00 current balance opening balance {frozen} 100.00 We assume this number is a prefix to the account number of all accounts, and the prefix never changes. This keyword indicates the value is locked and cannot be changed. Figure 1.11 Graphical representations for four types of data values:class variable,class constant,instance variable,and instance constant. constant wu23399_ch01.qxd 12/12/06 17:24 Page 22
  • 50. 1.4 Inheritance When we used the Account class and its instances to illustrate object-oriented con- cepts, some of you were probably thinking about checking accounts, while others may have been thinking about savings accounts. We did not distinguish between the two in the examples. But when we look at the problem a little more carefully, we will realize that in fact these two types of accounts are different, even though they share many features. In general, using only a single class to model two or more entities that are similar but different is not good design. In object-oriented programming, we use a mechanism called inheritance to design two or more entities that are different but share many common features. First we define a class that contains the common fea- tures of the entities. Then we define classes as an extension of the common class inheriting everything from the common class. We call the common class the superclass and all classes that inherit from it subclasses. We also call the superclass an ancestor and the subclass a descendant. Other names for superclass and subclass are base class and derived class, respectively. For the bank example, we can define a superclass Account and then define Savings and Checking as subclasses of Account. We represent the superclass and its subclasses as shown in Figure 1.12. Notice that we draw arrows from each subclass to its superclass because a subclass can refer to items defined in its superclass, but not vice versa. Inheritance is not limited to one level. A subclass can be a superclass of other classes, forming an inheritance hierarchy. Consider the example shown in Figure 1.13. Inheritance is very powerful, and if it is used properly, we can develop 1.4 Inheritance 23 1. What is the difference between a constant and a variable? 2. Draw an object diagram of a Person object with the three instance variables name, age, and gender. Account Savings Checking Figure 1.12 A superclass Account and its subclasses Savings and Checking. inheritance superclass and subclass wu23399_ch01.qxd 12/12/06 17:24 Page 23
  • 51. complex programs very efficiently and elegantly. The flip side of using a very pow- erful tool is that if we do not use it correctly, we could end up in a far worse situation than if we did not use it. We will be seeing many examples of inheritance through- out this book. In Chapter 2, for example, we will introduce many classes that come with the Java system. Most of these classes are defined using inheritance. We will provide an in-depth discussion of inheritance and related topics in Chapter 13. 24 Chapter 1 Introduction to Object-Oriented Programming and Software Development Student Law Doctoral Masters Graduate Undergraduate Commuting Resident Figure 1.13 An example of inheritance hierarchy among different types of students. 1. If Class A inherits from Class B, which is a superclass? Which is a subclass? 2. Draw a diagram that shows Class A is inheriting from Class B. 3. What are the other names for superclass and subclass? 4. If we have Animal, Insect, and Mammal classes, which one will be a superclass? 5. Model different types of vehicles, using inheritance. Include Vehicle, Automobile, Motorcycle, Sports Car, Sedan, and Bicycle. 1.5 Software Engineering and Software Life Cycle When we say computer programming, we are referring not only to writing Java commands, but also to a whole process of software development. Knowing a pro- gramming language alone is not enough to become a proficient software developer. wu23399_ch01.qxd 12/12/06 17:24 Page 24
  • 52. You must know how to design a program. This book will teach you how to design programs in an object-oriented manner. We construct a house in well-defined stages and apply the engineering princi- ples in all stages. Similarly, we build a program in stages and apply disciplined methodology in all stages of program development. The sequence of stages from conception to operation of a program is called the software life cycle, and software engineering is the application of a systematic and disciplined approach to the development, testing, and maintenance of a program. There are five major phases in the software life cycle: analysis, design, coding, testing, and operation. Software starts its life from the needs of a customer. A person wants an online address book, for example. In the analysis phase, we perform a fea- sibility study. We analyze the problem and determine whether a solution is possible. Provided that a solution is possible, the result of this phase is a requirements speci- fication that describes the features of a program. The features must be stated in a manner that is testable. One of the features for the address book program may be the capability to search for a person by giving his or her first name. We can test this feature by running the program and actually searching for a person. We verify that the program behaves as specified when the first name of a person in the address book and the first name of a person not in the address book are entered as a search condition. We do this testing in the testing phase, which we will explain shortly. In the design phase, we turn a requirements specification into a detailed design of the program. For an object-oriented design, the output from this phase will be a set of classes that fulfill the requirements. For the address book program, we may design classes such as Person, Phone, and others. In the coding phase, we implement the design into an actual program, in our case, a Java program. Once we have a well-constructed design, implementing it into actual code is really not that difficult. The difficult part is the creation of the design, and in this book, we place greater emphasis on the design aspect of the software construction. When the implementation is completed, we move to the testing phase. In this phase, we run the program, using different sets of data to verify that the program runs according to the specification. Two types of testing are possible for object- oriented programs: unit testing and integration testing. With unit testing, we test classes individually. With integration testing, we test that the classes work together correctly. Activity to eliminate programming error is called debugging. An error could be a result of faulty implementation or design. When there’s an error, we need to backtrack to earlier phases to eliminate the error. Finally, after the testing is successfully concluded, we enter the operation phase, in which the program will be put into actual use. The most important and time-consuming activity during the operation phase is software maintenance. After the software is put into use, we almost always have to make changes to it. For example, the customer may request additional features, or previously undetected er- rors may be found. Software maintenance means making changes to software. It is estimated that close to 70 percent of the cost of software is related to software main- tenance. So naturally, when we develop software, we should aim for software that is easy to maintain. We must not develop a piece of software hastily to reduce the 1.5 Software Engineering and Software Life Cycle 25 software life cycle software engineering analysis design coding testing operation software maintenance debugging wu23399_ch01.qxd 12/12/06 17:24 Page 25
  • 53. software development cost. We should take time and care to design and code soft- ware correctly even if it takes longer and costs more to develop initially. In the long run, carefully crafted software will have a lower total cost because of the reduced maintenance cost. Here’s an important point to remember: 26 Chapter 1 Introduction to Object-Oriented Programming and Software Development Well-designed and -constructed software is easy to maintain. In this book, we will focus on the design, coding, and testing phases. We will pre- sent a requirements specification in the form of a problem statement for the sample programs we will develop in this book. We present the first sample program devel- oped by following the design, coding, and testing phases in Chapter 2. We will come back to the discussion of software engineering and the software life cycle throughout the book and provide more details. 1. Name the stages of the software life cycle. 2. How does the quality of design affect the software maintenance cost? 3. What is debugging? • The style of programming we teach in this book is called object-oriented programming. • An object is an instance of a class. Many instances can be created from a single class. • There are class and instance methods. We can send messages to objects and classes if they possess matching methods. • There are class and instance data values. Data values are also called data members. • Inheritance is a powerful mechanism to model two or more entities that are different but share common features. • The sequence of software development stages from conception to operation is called the software life cycle. S u m m a r y wu23399_ch01.qxd 12/12/06 17:24 Page 26
  • 54. • Five major phases of the software life cycle are analysis, design, coding, testing, and operation. • Software engineering is the application of a systematic and disciplined approach to the development, testing, and maintenance of a program. Exercises 27 K e y C o n c e p t s object-oriented programming class object message class and instance methods instance and class data values variable constant inheritance superclass (ancestor, base class) subclass (descendant, derived class) software life cycle software engineering analysis design coding testing operation E x e r c i s e s 1. Graphically represent a Vehicle class and three Vehicle objects named car1, car2, and car3. 2. Graphically represent a Person class with the following components: • Instance variables name, age, and gender. • Instance methods setName, getName, and getAge. • Class method getAverageAge. 3. Design a CD class where a CD object represents a single music CD. What kinds of information (artist, genre, total playing time, etc.) do you want to know about a CD? Among the information in which you are interested, which are instance variables? Are there any class variables or class constants? 4. Suppose the Vehicle class in Exercise 1 is used in a program that keeps track of vehicle registration for the Department of Motor Vehicles. What kinds of instance variables would you define for such Vehicle objects? Can you think of any useful class variables for the Vehicle class? 5. Suppose the following formulas are used to compute the annual vehicle registration fee for the vehicle registration program of Exercise 4: • For cars, the annual fee is 2 percent of the value of the car. • For trucks, the annual fee is 5 percent of the loading capacity (in pounds) of the truck. Define two new classes Car and Truck as subclasses of Vehicle. Hint: Associate class and instance variables common to both Car and Truck to Vehicle. wu23399_ch01.qxd 12/12/06 17:24 Page 27
  • 55. 6. Consider a student registration program used by the registrar’s office. The program keeps track of students who are registered for a given semester. For each student registered, the program maintains the student’s name, address, and phone number; the number of classes in which the student is enrolled; and the student’s total credit hours. The program also keeps track of the total number of registered students. Define instance and class variables of a Student class that is suitable for this program. 7. Suppose the minimum number and maximum number of courses for which a student can register are different depending on whether the student is a graduate, undergraduate, or work/study student. Redo Exercise 6 by defining classes for different types of students. Relate the classes, using inheritance. 8. Imagine you are given the task of designing an airline reservation system that keeps track of flights for a commuter airline. List the classes you think would be necessary for designing such a system. Describe the data values and methods you would associate with each class you identify. Note: For this exercise and Exercises 9 through 12, we are not expecting you to design the system in complete detail. The objective of these exercises is to give you a taste of thinking about a program at a very high level. Try to identify about a half dozen or so classes, and for each class, describe several methods and data members. 9. Repeat Exercise 8, designing a university course scheduling system. The system keeps track of classes offered in a given quarter, the number of sections offered, and the number of students enrolled in each section. 10. Repeat Exercise 8, designing the state Department of Motor Vehicles registration system. The system keeps track of all licensed vehicles and drivers. How would you design objects representing different types of vehicles (e.g., motorcycles and trucks) and drivers (e.g., class A for commercial licenses and class B for towing vehicles)? 11. Repeat Exercise 8, designing a sales tracking system for a fast-food restaurant. The system keeps track of all menu items offered by the restaurant and the number of daily sales per menu item. 12. When you write a term paper, you have to consult many references: books, journal articles, newspaper articles, and so forth. Repeat Exercise 8, designing a bibliography organizer that keeps track of all references you used in writing a term paper. 13. Consider the inheritance hierarchy given in Figure 1.12. List the features common to all classes and the features unique to individual classes. Propose a new inheritance hierarchy based on the types of accounts your bank offers. 14. Consider a program that maintains an address book. Design an inheritance hierarchy for the classes such as Person, ProfessionalContact, Friend, and Student that can be used in implementing such a program. 15. Do you think the design phase is more important than the coding phase? Why or why not? 16. How does the quality of design affect the total cost of developing and maintaining software? 28 Chapter 1 Introduction to Object-Oriented Programming and Software Development wu23399_ch01.qxd 12/12/06 17:24 Page 28
  • 56. Getting Started with Java O b j e c t i v e s After you have read and studied this chapter,you should be able to • Identify the basic components of Java programs. • Write simple Java programs. • Describe the difference between object declaration and object creation. • Describe the process of creating and running Java programs. • Use the Date,SimpleDateFormat,String,and Scanner classes from the standard Java packages. • Develop Java programs,using the incremental development approach. 29 2 wu23399_ch02.qxd 12/12/06 17:26 Page 29
  • 57. e will describe the basic structure of simple Java programs in this chapter. We will also describe the steps you follow to run Java programs. We expect you to actually run these sample programs to verify that your computer (either your own or the one at the school’s computer center) is set up properly to run the sample programs presented in the book. It is important to verify this now. Otherwise, if you encounter a problem later, you won’t be able to determine whether the problem is the result of a bad program or a bad setup. Please check Appendix A for information on how to run the textbook’s sample programs. We will develop a sample application program in Section 2.4 following the design, coding, and testing phases of the software life cycle. We stress here again that our objective in this book is to teach object-oriented programming and how to apply object-oriented thinking in program development. The Java language is merely a means to implement a design into an executable program. We chose Java for this book because Java is a much easier language than other object-oriented pro- gramming languages to use to translate a design into an actual code. Beginning stu- dents often get lost in the language details and forget the main objective of learning the development process, but the use of Java should minimize this problem. 2.1 The First Java Program Our first Java application program displays a window on the screen, as shown in Fig- ure 2.1. The size of the window is set to 300 pixels wide and 200 pixels high. A pixel is a shorthand for picture element, and it is the standard unit of measurement for the screen resolution.Acommon resolution for a 17-in screen, for example, is 1024 pixels wide and 768 pixels high. The title of the window is set to My First Java Program. 30 Chapter 2 Getting Started with Java I n t r o d u c t i o n W pixel Figure 2.1 Result of running the Ch2Sample1 program.The window size is 300 by 200 pixels and has the title My First Java Program. wu23399_ch02.qxd 12/12/06 17:26 Page 30
  • 58. Although this program is very simple, it still illustrates the fundamental structure of an object-oriented program, which is as follows: 2.1 The First Java Program 31 An object-oriented program uses objects. It may sound too obvious, but let’s begin our study of object-oriented programming with this obvious notion. Here’s the program code: /* Chapter 2 Sample Program: Displaying a Window File: Ch2Sample1.java */ import javax.swing.*; class Ch2Sample1 { public static void main(String[] args) { JFrame myWindow; myWindow = new JFrame(); myWindow.setSize(300, 200); myWindow.setTitle(My First Java Program); myWindow.setVisible(true); } } This will not concern the majority of you, but if you are using a Java development tool that does not let you stop a running program easily, then insert the statement myWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); after myWindow.setVisible(true); so the program terminates automatically when the frame window is closed.Please read Appendix A for more information. wu23399_ch02.qxd 12/12/06 17:26 Page 31
  • 59. This program declares one class called Ch2Sample1, and the class includes one method called main. From this main method, the Ch2Sample1 class creates and uses a JFrame object named myWindow by sending the three messages setSize, setTitle, and setVisible to the object. The JFrame class is one of many classes that come with the Java system. An instance of this JFrame class is used to represent a single window on the computer screen. To differentiate the classes that program- mers define, including ourselves, and the predefined classes that come with the Java system, we will call the first programmer-defined classes and the latter Java stan- dard classes, or simply, standard classes. We also use the term system classes to refer to the standard classes. Expressing this program visually results in the program diagram shown in Figure 2.2. In this diagram, we draw individual messages, but doing so would eas- ily clutter a diagram when we have more than a handful of messages. Instead of drawing messages individually, we can draw one arrow to represent a dependency relationship. For this program, we say the Ch2Sample1 class is dependent on the services provided by a JFrame object, because the Ch2Sample1 class sends mes- sages to the MyWindow object. We draw a dotted arrow from Ch2Sample1 to myWindow to indicate the dependency relationship, as shown in Figure 2.3 We begin the explanation of the program from the following core five lines of code: JFrame myWindow; myWindow = new JFrame(); myWindow.setSize(300, 200); myWindow.setTitle(My First Java Program); myWindow.setVisible(true); 32 Chapter 2 Getting Started with Java programmer- defined classes standard classes program diagram dependency relationship myWindow : JFrame Ch2Sample1 setTitle(“My First Java Program”) setSize(300, 200) setVisible(true) Figure 2.2 The program diagram for the Ch2Sample1 program. Ch2Sample1 myWindow : JFrame Figure 2.3 The program diagram for the Ch2Sample1 program that shows the dependency relationship. wu23399_ch02.qxd 12/12/06 17:26 Page 32
  • 60. We will explain the rest of the program in Section 2.2. These five lines of code represent the crux of the program, namely, an object-oriented program that uses objects. The rule to remember in using objects is as follows: 2.1 The First Java Program 33 object declaration syntax identifier To use an object in a program,first we declare and create an object,and then we send messages to it. In the remainder of this section, we will describe how to declare an object, create an object, and use an object by sending messages to the object. Object Declaration Every object we use in a program must be declared. An object declaration desig- nates the name of an object and the class to which the object belongs. Its syntax is class name object names ; where object names is a sequence of object names separated by commas and class name is the name of a class to which these objects belong. Here’s how the general syntax is matched to the object declaration of the program: Class Name Object Names The class must be One object is defined beforehand. declared here. JFrame myWindow; Here are more examples: Account checking; Customer john, jack, jill; The first declaration declares an Account object named checking, and the second declaration declares three Customer objects. To declare an object as an instance of some class, the class must be defined already. First we will study how to use objects from system classes. Later in the book, we will show you how to define your own classes, from which you can create instances. When we declare an object, we must give it a name. Any valid identifier that is not reserved for other uses can be used as an object name. A Java identifier is a se- quence of letters, digits, underscores (_), and dollar signs ($) with the first one being wu23399_ch02.qxd 12/12/06 17:26 Page 33
  • 61. a letter. We use an identifier to name a class, object, method, and others. The following words are all valid identifiers: MyFirstApplication FunTime ComputeArea DEFAULT_VALUE Upper- and lowercase letters are distinguished, so the following four identi- fiers are distinct: myWindow mywindow MYwindow MYWINDOW No spaces are allowed in an identifier, and therefore, the three lines Sample Program My First Application Program FunTime are all invalid identifiers. Since upper- and lowercase letters are distinguished, you can use robot as the name for an object of the class Robot. We name objects in this manner whenever possible in this book so we can easily tell to which class the object belongs. We fol- low the Java standard naming convention of using an uppercase letter for the first letter of the class names and a lowercase letter for the first letter of the object names in this book. It is important to follow the standard naming convention so others who read your program can easily distinguish the purposes of identifiers. Programs that follow the standard naming convention are easier to read than those that do not. And remember that software maintenance is easier with easy-to-understand programs. When an identifier consists of multiple words, the Java naming convention dictates the first letter from every word, except the first word, will be capitalized, for example, myMainWindow, not mymainwindow. 34 Chapter 2 Getting Started with Java standard naming convention Follow the standard naming convention in writing your Java programs to make them easier to read. Table 2.2 in the Summary section summarizes the naming convention. Object Creation No objects are actually created by the declaration. An object declaration simply declares the name (identifier) that we use to refer to an object. For example, the declaration JFrame myWindow; wu23399_ch02.qxd 12/12/06 17:26 Page 34
  • 62. designates that the name myWindow is used to refer to a JFrame object, but the actual JFrame object is not yet created. We create an object by invoking the new operator. The syntax for new is object name = new class name ( arguments ) ; where object name is the name of a declared object, class name is the name of the class to which the object belongs, and arguments is a sequence of values passed to the new operation. Let’s match the syntax to the actual statement in the sample program: Object Name Class Name Name of the object An instance of this we are creating class is created. myWindow = new JFrame ( ) ; Argument No arguments are used here. Figure 2.4 shows the distinction between object declaration and creation. Figure 2.5 shows the relationship between the UML-based program diagram and the state-of-memory diagram. The state-of-memory diagram borrows the notation from UML for consistency, but it is not a true UML diagram because it uses sym- bols and notations not found in UML. Now, consider the following object declaration and two statements of object creation: Customer customer; customer = new Customer( ); customer = new Customer( ); What do you think will happen? An error? No. It is permissible to use the same name to refer to different objects of the same class at different times. Figure 2.6 2.1 The First Java Program 35 new operator object creation syntax Instead of writing statements for object declaration and creation separately, we can combine them into one statement. We can write,for example, Student john = new Student(); instead of Student john; john = new Student(); wu23399_ch02.qxd 12/12/06 17:26 Page 35
  • 63. shows the state-of-memory diagram after the second new is executed. Since there is no reference to the first Customer object anymore, eventually it will be erased and returned to the system. Remember that when an object is created, a certain amount of memory space is allocated for storing this object. If this allocated but unused space is not returned to the system for other uses, the space gets wasted. This 36 Chapter 2 Getting Started with Java State-of-Memory Notation Program Diagram Notation account : Account account :Account The state-of-memory diagram uses the same UML notation, but it also includes symbols and notations not found in UML. Figure 2.5 Relationship between the state-of-memory diagram and the program diagram notation. State of Memory Account account; account = new Account( ); A Account account; B account = new Account( ); after is executed A account after is executed B The identifier account is declared and space is allocated in memory. An Account object is created and the identifier account is set to refer to it. account :Account Figure 2.4 Distinction between object declaration and object creation. wu23399_ch02.qxd 12/12/06 17:26 Page 36
  • 64. returning of space to the system is called deallocation, and the mechanism to deal- locate unused space is called garbage collection. Message Sending After the object is created, we can start sending messages to it. The syntax for send- ing a message to an object is object name . method name ( arguments ) ; where object name is an object name, method name is the name of a method of the object, and arguments is a sequence of values passed to the method. In the sample program, we send the setVisible message with the argument true to the mainWindow object to make it appear on the screen. Once again, let’s match the components in the general syntax to the actual statement: Object Name Method Name Name of the object to which The name of the message we are sending a message we are sending myWindow . setVisible ( true ) ; Argument The argument we are passing with the message Figure 2.7 shows the correspondence between message sending as repre- sented in the program diagram and in the Java statement. Because the object that receives a message must possess a corresponding method, we often substitute the expression sending a message with calling a method. We will use these expressions interchangeably. 2.1 The First Java Program 37 customer :Customer :Customer The first Customer object will be deallocated eventually because there are no references to it anymore. Created with the second new. Created with the first new. Customer customer; customer = new Customer(); customer = new Customer(); Figure 2.6 The state after two new commands are executed. garbage collection message- sending syntax wu23399_ch02.qxd 12/12/06 17:26 Page 37
  • 65. Notice the argument for the setVisible message does not include double quotes as did the one for the setTitle message in the example shown on page 32. The argu- ment true is one of the two possible logical values (the other is false) used in Java programs. We will study more about the use of logical values later in the book, start- ing from Chapter 5. For now, it suffices to remember that there are two logical values— true and false—used for certain specific purposes. Passing true in the setVisible message makes the receiving object appear on the screen. Passing false makes the object disappear from the screen. So, for exam- ple, if we write myWindow.setVisible(true); myWindow.setVisible(false); myWindow.setVisible(true); then myWindow will appear once, disappear, and then appear on the screen again. (Note: Because the computer will execute these statements so quickly, you may not notice any difference from the original program. See Exercise 22 on page 77.) The word true (and false) is called a reserved word. It is an identifier that is used for a specific purpose and cannot be used for any other purpose, such as for the name of an object. 38 Chapter 2 Getting Started with Java myWindow:JFrame setVisible(true) Program Diagram Corresponding Java Statement Note: We can place method icons on either side of a class or instance icon. myWindow . setVisible ( true ) ; Figure 2.7 Correspondence between message sending as represented in the program diagram and in the actual Java statement. The expression calling object O’s method M is synonymous with sending message M to object O. reserved word 1. Which of the following are invalid identifiers? a. one b. my Window c. 1234 wu23399_ch02.qxd 12/12/06 17:26 Page 38
  • 66. d. DecafeLattePlease e. hello f. JAVA g. hello, there h. acct122 2. What’s wrong with the following code? JFrame myWindow(); myWindow.setVisible(true); 3. Is there anything wrong with the following declarations? mainWindow MainWindow; Account, Customer account, customer; 4. Which of the following statements is valid? a. myFirstWindow.setVisible( true ); b. myFirstWindow.setVisible( true ); 2.2 Program Components Now that we have covered the crux of the first sample program, let’s examine the rest of the program. The first sample application program Ch2Sample1 is composed of three parts: comment, import statement, and class declaration. These three parts are included universally in Java programs. 2.2 Program Components 39 comments A Java program is composed of comments, import statements,and class declarations. You can write a Java program that includes only a single class declaration, but that is not the norm. In any nontrivial program, you will see these three components. We explain the three components and their subparts in this section. Comments In addition to the instructions for computers to follow, programs contain comments in which we state the purpose of the program, explain the meaning of code, and pro- vide any other descriptions to help programmers understand the program. Here’s wu23399_ch02.qxd 12/12/06 17:26 Page 39
  • 67. the comment in the sample Ch2Sample1 program: 40 Chapter 2 Getting Started with Java /* Chapter 2 Sample Program: Displaying a Window File: Ch2Sample1.java */ import javax.swing.*; class Ch2Sample1 { public static void main(String[] args) { JFrame myWindow; myWindow = new JFrame(); myWindow.setSize(300, 200); myWindow.setTitle(My First Java Program); myWindow.setVisible(true); } } Comment A comment is any sequence of text that begins with the marker /* and termi- nates with another marker */. The beginning and ending comment markers are matched in pairs; that is, every beginning marker must have a matching ending marker. A beginning marker is matched with the next ending marker that appears. Any beginning markers that appear between the beginning marker and its matching ending marker are treated as part of the comment. In other words, you cannot put a comment inside another comment. The examples in Figure 2.8 illustrate how the matching is done. Another marker for a comment is double slashes //. This marker is used for a single-line comment marker. Any text between the double-slash marker and the end of a line is a comment. The following example shows the difference between mul- tiline and single-line comments: /* This is a comment with three lines of text. */ // This is a comment // This is another comment // This is a third comment comment markers single-line comment marker wu23399_ch02.qxd 12/12/06 17:26 Page 40
  • 68. The third type of comment is called a javadoc comment. It is a specialized comment that can appear before the class declaration and other program elements yet to be described in the book. We will explain more about javadoc comments in Chapter 7. Comments are intended for the programmers only and are ignored by the computer. Therefore, comments are really not necessary in making a program exe- cutable, but they are an important aspect of documenting the program. It is not enough to write a program that executes correctly. We need to document the pro- gram, and commenting the program is an important part of program documentation. Other parts of program documentation include program diagrams, programmers’ work logs, design documents, and user manuals. If you can write a program once and use it forever without ever modifying it, then writing a program with no com- ments may be tolerable. However, in the real world, using programs without ever making any changes almost never happens. For example, you may decide to add new features and capabilities or modify the way the user interacts with the program. Even if you don’t improve the program, you still have to modify the program when you detect some errors in it. Also, for commercial programs, those who change the programs are most often not the ones who developed them. When the time comes 2.2 Program Components 41 javadoc comment */ /* This is a comment on one line */ /* Comment number 1 */ /* Comment number 2 */ /* /* /* This is a comment */ . An error: no matching beginning marker These two markers are part of the comment. Figure 2.8 How the beginning and ending comment markers are matched. Although not required to run the program,comments are indispensable in writing easy-to-understand code. wu23399_ch02.qxd 12/12/06 17:26 Page 41
  • 69. for a programmer to modify his own or someone else’s program, the programmer must first understand the program, and program documentation is an indispensable aid to understanding the program. There are several different uses of comments. The first is the header comment. At the beginning of a program, we place a comment to describe the program. We characterize such a comment as a header comment. We also may include header comments at the beginning of methods to describe their purposes. Depending on the length and complexity of programs, the description may range from short and sim- ple to long and very detailed. A typical header comment for a beginning program- ming class may look something like this: 42 Chapter 2 Getting Started with Java /* * Program: TextEditor * * Author: Decafe Latte * [email protected] * * Written: May 1, 2006 * * Course: Comp Sci 101 * Spring 2006 * Program Assignment No. 7 * * Compiler: JDK 1.5 * Platform: Windows XP * * Description: * This is a simple text editor. The editor allows the user * to save text to a file and read text from a file. The * editor displays text using Courier font only and does not * allow formatting (e.g., bold, italic, etc.). The editor * supports standard editing functions Cut, Copy, and * Paste, but does not support Undo. For more details, * please refer to the TxEditReadme file. */ header comment typical header comment for a beginning programming class Note: The use of the asterisks is in the style of javadoc,but this is not a javadoc comment. For your own programs, you should write header comments following the guideline provided by your instructor. For listing the sample programs in the book, we will include only the program name and a short description in the header com- ment, mainly for reference purposes. The header comment in the actual programs, available from our website, includes additional information. wu23399_ch02.qxd 12/12/06 17:26 Page 42
  • 70. Another use of comments is to explain code whose purpose may not be obvi- ous. Your aim is always to write easily understandable, self-explanatory program code. But at times this is not possible, and you should attach comment to code that is not so easy to understand. There also are times when the original code may not work as intended, and as a temporary measure, you modify the code slightly so the program will continue to work. You should clearly mark such modification with a comment, so you remember what you have done. If you did not put in an appropri- ate comment and later read your code without remembering about the modification, you would have no idea why you wrote such code. If you cannot understand your own code, imagine the frustration of other programmers (or your T.A. or instructor) trying to understand your modified code. Yet another use of comments is to identify or summarize a block of code. Sup- pose a program is divided into three major parts: getting input values from the user, performing computation by using those values, and displaying the computation re- sults. You can place comments at the top of each part to delineate the three major parts clearly. Remember that adding comments to a poorly designed program will not make it a better program. Your foremost goal is to develop a well-designed program that runs efficiently and is easy to understand. Commenting a program is only a means toward that goal, not a goal itself. In fact, excessive use of comments makes it harder to follow and understand a program. 2.2 Program Components 43 package Always aim for self-explanatory code.Do not attempt to make poorly written code easier to read by comments.Good comments are not a substitute for good code.Bad code is bad,no matter how well your comments are written. Comment markers are useful in disabling a portion of a program.Let’s say you find a portion that may be causing the program to crash, and you want to try out different code for the problem portion. Instead of replacing the whole problem portion with new code, you can leave the questionable code in the program by converting it into a “comment” with comment markers. You can remove the comment markers if you need this code later. Import Statement We develop object-oriented programs by using predefined classes, both system- and programmer-defined, whenever possible and defining our own classes when no suitable predefined classes are available. In Java, classes are grouped into packages, and the Java system comes with numerous packages. We also can logi- cally group our own classes into a package so they can be reused conveniently by other programs. wu23399_ch02.qxd 12/12/06 17:26 Page 43
  • 71. To use a class from a package, we refer to the class in our program by using the following format: package name . class name For example, to use the Resident class in the dorm package, we refer to it as dorm.Resident which we read as “dorm dot Resident.” This notation is called dot notation. A package can include subpackages, forming a hierarchy of packages. In re- ferring to a class in a deeply nested package, we use multiple dots. For example, we write javax.swing.JFrame to refer to the class JFrame in the javax.swing package; that is, the swing package is inside the javax package. Dot notation with the names of all packages to which a class belongs is called the class’s fully qualified name. Using the fully qualified name of a class is frequently too cumbersome, especially when we have to refer to the same class many times in a program. We can use the import statement to avoid this problem. Here’s the original Ch2Sample1 program that uses the import statement: 44 Chapter 2 Getting Started with Java dot notation fully qualified name /* Chapter 2 Sample Program: Displaying a Window File: Ch2Sample1.java */ import javax.swing.*; class Ch2Sample1 { public static void main(String[] args) { JFrame myWindow; myWindow = new JFrame(); myWindow.setSize(300, 200); myWindow.setTitle(My First Java Program); myWindow.setVisible(true); } } Import Statement The import statement allows the program to refer to classes defined in the designated pack- age without using the fully qualified class name. wu23399_ch02.qxd 12/12/06 17:26 Page 44
  • 72. And here’s the same Ch2Sample1 program without the import statement: 2.2 Program Components 45 /* Chapter 2 Sample Program: Displaying a Window File: Ch2Sample1.java */ class Ch2Sample1 { public static void main(String[] args) { javax.swing.JFrame myWindow; myWindow = new javax.swing.JFrame (); myWindow.setSize(300, 200); myWindow.setTitle(My First Java Program); myWindow.setVisible(true); } } No import statement Fully qualified names Instead of using the expression javax.swing.JFrame to refer to the class, we can refer to it simply as JFrame by including the import statement import javax.swing.JFrame; at the beginning of the program. Notice that the import statement is terminated by a semicolon. If we need to import more than one class from the same package, then instead of using an import statement for every class, we can import them all by using asterisk notation: import package name . * ; For example, if we state import javax.swing.*; wu23399_ch02.qxd 12/12/06 17:26 Page 45
  • 73. then we are importing all classes from the javax.swing package. We use this asterisk notation in our sample program, even when we use only one of the many classes available in the javax.swing package. We could have used import javax.swing.JFrame; but it is more conventional to use asterisk notation. Notice that the package names are all in lowercase letters. This is another standard Java naming convention. Chapter 4 includes greater discussion of packages. 46 Chapter 2 Getting Started with Java When we say “import a package,” it sounds as if we are copying all those classes into our programs. That is not the case. Importing a package is only a shorthand notation for referencing classes.The only effect of importing a package is the elim- ination of the requirement to use the fully qualified name.No classes are physically copied into our programs. Class Declaration A Java program is composed of one or more classes; some are predefined classes, while others are defined by us. In the first sample program, there are two classes— JFrame and Ch2Sample1. The JFrame class is one of the standard classes, and the Ch2Sample1 class is the class we define ourselves. To define a new class, we must declare it in the program, or make a class declaration. The syntax for declaring the class is class class name { class member declarations } where class name is the name of the class and class member declarations is a sequence of class member declarations. The word class is a reserved word used to mark the beginning of a class declaration. A class member is either a data value or a method. We can use any valid identifier that is not reserved to name the class. Here’s the class declaration in the sample Ch2Sample1 program: class declaration /* Chapter 2 Sample Program: Displaying a Window File: Ch2Sample1.java */ import javax.swing.*; wu23399_ch02.qxd 12/12/06 17:26 Page 46
  • 74. One of the classes in a program must be designated as the main class. The main class of the sample program is Ch2Sample1. Exactly how you designate a class as the main class of the program depends on which Java program development tool you use. We will use the name of a main class to refer to a whole application. For example, we say the Ch2Sample1 class when we refer to the class itself, and we say the Ch2Sample1 application when we refer to the whole application. If we designate a class as the main class, then we must define a method called main, because when a Java program is executed, the main method of a main class is executed first. To define a method, we must declare it in a class. Method Declaration The syntax for method declaration is modifiers return type method name ( parameters ) { method body } where modifiers is a sequence of terms designating different kinds of methods, return type is the type of data value returned by a method, method name is the name of a method, parameters is a sequence of values passed to a method, and method body is a sequence of instructions. Here’s the method declaration for the main method: 2.2 Program Components 47 main class method declaration class Ch2Sample1 { public static void main(String[] args) { JFrame myWindow; myWindow = new JFrame(); myWindow.setSize(300, 200); myWindow.setTitle(My First Java Program); myWindow.setVisible(true); } } Class Declaration Every program must include at least one class. /* Chapter 2 Sample Program: Displaying a Window File: Ch2Sample1.java */ wu23399_ch02.qxd 12/12/06 17:26 Page 47
  • 75. 48 Chapter 2 Getting Started with Java import javax.swing.*; class Ch2Sample1 { public static void main(String[] args) { JFrame myWindow; myWindow = new JFrame(); myWindow.setSize(300, 200); myWindow.setTitle(My First Java Program); myWindow.setVisible(true); } } Method Declaration This declaration declares the main method. Let’s match these components to the actual method declaration of the sample program: We do not explain the meanings of modifiers, return types, and parameters here. We will explain them in detail gradually as we progress through the book. For now, we ask you to follow a program template that we present next. A Program Template for Simple Java Applications The diagram in Figure 2.9 shows a program template for simple Java applications. You can follow this program template to write very simple Java applications. The structure of the sample program Ch2Sample1 follows this template. Method Body Consists of a sequence of instructions Parameter Modifier Modifier Method Name Return Type JFrame myWindow; myWindow = new JFrame(); myWindow.setSize(300, 200); myWindow.setTitle(My First Java Program); myWindow.setVisible(true); public } static void main ( String[] args ) { wu23399_ch02.qxd 12/12/06 17:26 Page 48
  • 76. 2.3 Edit-Compile-Run Cycle We will walk through the steps involved in executing the first sample program. What we outline here are the overall steps in the edit-compile-run cycle common to any Java development tool you use. You need to get detailed instructions on how to use your chosen development tool to actually run programs. The steps we present in this section should serve as a guideline for more detailed instructions specific to your program development tool. Additional information on how to run Java pro- grams can be found in Appendix A. Step 1 Type in the program, using an editor, and save the program to a file. Use the name of the main class and the suffix .java for the filename. This file, in which the pro- gram is in a human-readable form, is called a source file. 2.3 Edit-Compile-Run Cycle 49 Comment Use a comment to describe the program. Import Statements Include a sequence of import statements. public static void main(String[] args) { } } class { Class Name Give a descriptive name to the main class. Method Body Include a sequence of instructions. Figure 2.9 A program template for simple Java applications. edit-compile- run cycle source file /* Chapter 2 Sample Program: Displaying a Window File: Ch2Sample1.java */ Ch2Sample1.java wu23399_ch02.qxd 12/12/06 17:26 Page 49
  • 77. Step 2 Compile the source file. Many compilers require you to create a project file and then place the source file in the project file in order to compile the source file. When the compilation is successful, the compiled version of the source file is created. This compiled version is called bytecode, and the file that contains bytecode is called a bytecode file. The name of the compiler-generated bytecode file will have the suffix .class while its prefix is the same as the one for the source file. When any error occurs in a program, an error message will be displayed. If the sample program contains no errors in syntax, then instead of an error message, you will get nothing or a message stating something like “Compiled successfully.” To see what kind of error messages are displayed, try compiling the following program. We purposely introduced three errors. Can you find them? Make sure to compile the correct Ch2Sample1 again before you proceed to the next step. /* Chapter 2 Sample Program: Displaying a Window File: Ch2Sample.java */ import javax.swing.*; class Ch2Samplel { public static void main( String[] args ) { JFrame myWindow; myWindow = new JFrame(); be 00 03 00 2d 00 1f 08 00 12 07 00 0c ..兰....–........ 000010 07 00 15 07 00 13 0a 00 04 00 08 0a 00 03 00 07 ................ 000020 0c 00 19 00 1c 0c 00 17 00 14 01 00 04 74 68 69 .............thi 000030 73 01 00 0d 43 6f 6e 73 74 61 6e 74 56 61 6c 75 s...ConstantValu 000040 65 01 00 12 4c 6f 63 61 6c 56 61 72 69 61 62 6c e...LocalVariabl 000050 65 54 61 62 6c 65 01 00 0e 6d 79 46 69 72 73 74 eTable...myFirst 000060 50 72 6f 67 72 61 6d 01 00 0a 45 78 63 65 70 74 Program...Except 000070 69 6f 6e 73 01 00 0f 4c 69 6e 65 4e 75 6d 62 65 ions...LineNumbe 000080 72 54 61 62 6c 65 01 00 0a 53 6f 75 72 63 65 46 rTable...Source 000090 69 6c 65 01 00 0e 4c 6f 63 61 6c 56 61 72 69 61 ile...LocalVaria 0000a0 62 6c 65 73 01 00 04 43 6f 64 65 01 00 0b 49 20 bles...Code...I 0000b0 4c 6f 76 65 20 4a 61 76 61 01 00 13 4a 61 76 61 Love Java...Java 0000c0 42 6f 6f 6b 2f 4d 65 73 73 61 67 65 42 6f 78 01 Book/MessageBox. 0000d0 00 15 28 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 ..(Ljava/lang/St 0000e0 72 69 6e 67 3b 29 56 01 00 10 6a 61 76 61 2f 6c ring;)V...java/1 0000f0 61 6e 67 2f 4f 62 6a 65 63 74 01 00 04 6d 61 69 ang/Object...mai 000100 6e 01 00 07 64 69 73 70 6c 61 79 01 00 16 28 5b n...display...([ 000110 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e Ljava/lang/Strin 000120 67 3b 29 56 01 00 06 3c 69 6e 69 74 3e 01 00 10 g;)V...init... 000130 4c 6d 79 46 69 72 73 74 50 72 6f 67 72 61 6d 3b LmyFirstProgram; 000140 01 00 13 6d 79 46 69 72 73 74 50 72 6f 67 72 61 ...myFirstProgra 000150 6d 2e 6a 61 76 61 01 00 03 28 29 56 01 00 04 61 m.java...()V...a 000160 72 67 73 01 00 13 5b 4c 6a 61 76 61 2f 6c 61 6e rgs...[Ljava/lan 000170 67 2f 53 74 72 69 6e 67 3b 00 00 00 02 00 03 00 g/String;....... Compiler Ch2Sample1.java (source file) Ch2Sample1.class (bytecode file) Editor 50 Chapter 2 Getting Started with Java import javax.swing.*; class Ch2Sample1 { public static void main( String[] args ) { JFrame myWindow; myWindow = new JFrame(); myWindow.setSize(300, 200); myWindow.setTitle(My First Java Program); myWindow.setVisible(true); } } Editor (source file) project file bytecode bytecode file wu23399_ch02.qxd 12/12/06 17:26 Page 50
  • 78. Bad Version import javax.swing.*; class Ch2Sample1 { public static void main( String[]args ) { myWindow = new JFrame(); myWindow.setSize( ); myWindow.setTitle(My First Java Program); myWindow.setVisible(true) } } Errors detected by the compiler are called compilation errors. Compilation errors are actually the easiest type of errors to correct. Most compilation errors are due to the violation of syntax rules. Step 3 Execute the bytecode file. A Java interpreter will go through the bytecode file and execute the instructions in it. If your program is error-free, a window will appear on the screen. If an error occurs in running the program, the interpreter will catch it and stop its execution. Errors detected by the interpreter are called execution errors. If you did not see the expected results, go back to the previous steps and verify that your program is entered correctly. If you still do not see the expected results, then most likely your development environment is not set up correctly. Please refer to other sources of information for further help. be 00 03 00 2d 00 1f 08 00 12 07 00 0c ..兰....–........ 000010 07 00 15 07 00 13 0a 00 04 00 08 0a 00 03 00 07 ................ 000020 0c 00 19 00 1c 0c 00 17 00 14 01 00 04 74 68 69 .............thi 000030 73 01 00 0d 43 6f 6e 73 74 61 6e 74 56 61 6c 75 s...ConstantValu 000040 65 01 00 12 4c 6f 63 61 6c 56 61 72 69 61 62 6c e...LocalVariabl 000050 65 54 61 62 6c 65 01 00 0e 6d 79 46 69 72 73 74 eTable...myFirst 000060 50 72 6f 67 72 61 6d 01 00 0a 45 78 63 65 70 74 Program...Except 000070 69 6f 6e 73 01 00 0f 4c 69 6e 65 4e 75 6d 62 65 ions...LineNumbe 000080 72 54 61 62 6c 65 01 00 0a 53 6f 75 72 63 65 46 rTable...Source /* Chapter 2 Sample Program: Displaying a Window File: Ch2Sample.java */ import javax.swing.*; class Ch2Samplel { public static void main( String[] args ) { JFrame myWindow; myWindow = new JFrame(); Compiler Interpreter Ch2Sample1.java (source file) Ch2Sample1.class (bytecode file) Running Program Editor 2.3 Edit-Compile-Run Cycle 51 compilation error execution error wu23399_ch02.qxd 12/12/06 17:26 Page 51
  • 79. 52 Chapter 2 Getting Started with Java Unlike machine-language instructions, or machine code, Java bytecode is not tied to any particular operating system or CPU. All we need to run the same Java pro- grams on different operating systems is the Java interpreters for the desired oper- ating systems. Currently, there are Java interpreters for Windows, Mac, Unix, and other operating systems. A Java interpreter is also called a Java Virtual Machine (JVM) because it is like a virtual machine that executes bytecode, whereas a CPU is a real machine that executes machine code. 2.4 Sample Java Standard Classes Eventually, you must learn how to define your own classes, the classes you will reuse in writing programs. But before you can become adept at defining your own classes, you must learn how to use existing classes. In this section, we will intro- duce standard classes. Sample code using these classes helps us reinforce the core object-oriented programming (OOP) concepts introduced in Chapter 1 with the actual Java statements. Five standard classes we introduce here are JOptionPane, System, Scanner String, Date, and SimpleDateFormat. It is not our objective here to explain these classes fully. Rather, our objective is to get you started in writing practical Java programs with a minimal explanation of some of the useful standard classes. We will introduce additional capabilities of these classes as we progress through the textbook. Although we will scratch only the surface of these classes in this section, what we provide here should serve as a foundation for you to delve more deeply into these classes. For a more detailed description, please consult the documentation for the standard classes. The documentation for the standard classes is commonly called Java API documentation, where API stands for application programming interface. To become a good object-oriented programmer,first you must learn how to use predefined classes. Please do not get alarmed by the number of standard classes we introduce here. Although we cover five standard classes at once, we limit ourselves to the most basic operations, so we won’t overwhelm you with too much information. Their documentation can be located online at http://java.sun.com/j2se/1.5.0/docs/api/index.html wu23399_ch02.qxd 12/12/06 17:26 Page 52
  • 80. 2.4.1 Standard Output When a program computes a result, we need a way to display this result to the user of the program. One of the most common ways to do this in Java is to use the console window. The console window is also called the standard output window. We output data such as the computation results or messages to the con- sole window via System.out. The System class includes a number of useful class data values. One is an instance of the PrintStream class named out. Since this is a class data value, we refer to it through the class name as System.out, and this PrintStream object is tied to the console window (there’s exactly one console window per program). Every data item we send to System.out will appear on this console window. We call the technique to output data by using System.out the standard output. We use the print method to output a value. For example, executing the code System.out.print(Hello, Dr. Caffeine.); will result in the console window shown in Figure 2.10. The actual appearance of the console window will differ depending on which Java development tool we use. Despite the difference in the actual appearance, its functionality of displaying data is the same among different Java tools. 2.4 Sample Java Standard Classes 53 console window System.out standard output Note Depending on the tool you use, you may see additional text such as Press any key to continue... or something similar to it. We will ignore any text that may be displayed automatically by the system. Figure 2.10 Result of executing System.out.print(Hello,Dr.Caffeine.). System.out refers to a precreated PrintStream object we use to output data to the console window.The actual appearance of the console window depends on which Java tool we use. wu23399_ch02.qxd 12/12/06 17:26 Page 53
  • 81. The print method will continue printing from the end of the currently dis- played output. Executing the following statements will result in the console window shown in Figure 2.11. System.out.print(How do you do? ); System.out.print(My name is ); System.out.print(Seattle Slew.); Notice that they all appear on the same line. If we want them to appear on individual lines, we can use the println method instead of print. The word println is a short- hand for “print line.” Figure 2.12 shows the effect of the println method. This concludes our quick introduction to System.out. We will be gradually in- troducing additional techniques of outputting to the console window as they are needed. 54 Chapter 2 Getting Started with Java System.out.print(How do you do? ); System.out.print(My name is ); System.out.print(Seattle Slew.); How do you do? My name is Seattle Slew. Code Output Note: Because the actual appearance of the console window is different depending on the Java development tool you use, we use a generic picture for the console window in the diagrams. Figure 2.11 Result of executing three consecutive print methods. The print method continues the printing from the currently displayed output. How do you do? My name is Seattle Slew. System.out.println(How do you do? ); System.out.println(My name is ); System.out.println(Seattle Slew.); Code Output Figure 2.12 Result of executing three consecutive println methods.The println method will skip to the next line after printing out its argument. wu23399_ch02.qxd 1/11/07 11:47 Page 54
  • 82. 2.4 Sample Java Standard Classes 55 1. Write a Java statement to display the text I Love Java in the console window. 2. Write statements to display the following shopping list in the console window. Don’t forget to include blank spaces so the item names appear indented. Shopping List: Apple Banana Low-fat Milk 2.4.2 String The textual values we passed to the print method or the constructor of the JFrame class are instances of the String class. A sequence of characters separated by double quotes is String constants. As String is a class, we can create an instance and give it a name. For example, String name; name = new String(Jon Java); will result in a situation as follows: Unlike in other classes, the explicit use of new to create an instance is optional for the String class. We can create a new String object, for example, in this way: String name; name = Decafe Latte; There are close to 50 methods defined in the String class. We will introduce three of them here: substring, length, and indexOf. We can extract a substring from a given string by specifying the beginning and ending positions. For example, String text; text = Espresso; System.out.print(text.substring(2, 7)); will display the dialog shown in Figure 2.13. name Jon Java :String substring wu23399_ch02.qxd 12/12/06 17:26 Page 55
  • 83. Notice the use of method composition in the last statement, where the result of a method call is used as an argument in another method call. In the statement System.out.print (text.substring(2,7)); the result of method call text.substring(2,7) is passed as an argument when calling the showMessageDialog method. The sample statement is equivalent to String tempStr; tempStr = text.substring(2,7); System.out.print(tempStr); Individual characters in a String object are indexed from 0, as illustrated in Figure 2.14. The first argument of the substring method specifies the position of the first character, and the second argument specifies the value that is 1 more than the 56 Chapter 2 Getting Started with Java press String text; text = Espresso; System.out.print(text.substring(2,7)); Code Output Figure 2.13 Result of extracting and displaying the substring of Expresso from index position’s 2 to 6. The index position of the first character in a string is 0. String text; text = Espresso; E s p r e s s o 0 1 2 3 4 5 6 7 Figure 2.14 Individual characters in a string are numbered from 0. method composition wu23399_ch02.qxd 1/11/07 11:47 Page 56
  • 84. An error will result if you pass invalid arguments, such as negative values, the sec- ond argument larger than the number of characters in a string, or the first argument larger than the second argument. We can find out the number of characters in a String object by using the length method. For example, if the name text refers to a string Espresso, then text.length() will return the value 8, because there are eight characters in the string. Here are some more examples: 2.4 Sample Java Standard Classes 57 p r e s s 0 1 2 3 4 E s p r e s s o 0 1 2 3 4 5 6 7 text.substring(2, 7) Figure 2.15 The effect of the substring method is shown.Notice that a new string is created, and the original string remains intact. length text.substring( 6, 8 ) so text.substring( 0, 8 ) Espresso text.substring( 1, 5 ) spre text1 = ; //empty string text2 = Hello; text3 = Java; text1.length( ) 0 text2.length( ) 5 text3.length( ) 4 position of the last character. Figure 2.15 shows how the substring method works. Here are some more examples: wu23399_ch02.qxd 12/12/06 17:26 Page 57
  • 85. To locate the index position of a substring within another string, we use the indexOf method. For example, if the name text refers to a string I Love Java, then text.indexOf(Love) will return the value 2, the index position of the first character of the designated string Love. If the searched substring is not located in the string, then 1 is returned. Notice that the search is done in a case-sensitive manner. Thus, text.indexOf(java) will return 1. If there is more than one occurrence of the same substring, the index position of the first character of the first matching substring is returned. Here are some more examples: 58 Chapter 2 Getting Started with Java indexOf string concatenation text = I Love Java and Java loves me.; text.indexOf(J) 7 text.indexOf(love) 21 text.indexOf(ove) 3 text.indexOf(ME) -1 3 7 21 Beyond the three methods we cover here and the remaining methods of the String class, we have one very useful string operation in Java called string concate- nation. We can create a new string from two strings by concatenating the two strings. We use the plus symbol () for string concatenation. Here are the examples: text1 = Jon; text2 = Java; text1 + text2 JonJava text1 + + text2 Jon Java How are you, + text1 + ? How are you, Jon? wu23399_ch02.qxd 12/12/06 17:26 Page 58
  • 86. The sample class Ch2StringProcessing divides the given full name into the first and last names and displays the number of letters in the last name. 2.4 Sample Java Standard Classes 59 /* Chapter 2 Sample Program: Simple String Processing File: Ch2StringProcessing.java */ class Ch2StringProcessing { public static void main( String[] args ) { String fullName, firstName, lastName, space; fullName = new String(Decafe Latte); space = new String( ); firstName = fullName.substring(0, fullName.indexOf(space)); lastName = fullName.substring(fullName.indexOf(space) + 1, fullName.length()); System.out.println(Full Name: + fullName); System.out.println(First: + firstName); System.out.println(Last: + lastName); System.out.println(Your last name has + lastName.length( ) + characters.); } } 1. What will be the value of mystery when the following code is executed? String text, mystery; text = mocha chai latte; mystery = text.substring(1,5); 2. What will be displayed on the message dialog when the following code is executed? String text = I, Claudius; System.out.println(text.indexOf(I) ); wu23399_ch02.qxd 12/12/06 17:26 Page 59
  • 87. 3. What will be displayed on the message dialog when the following code is executed? String text = Augustus; System.out.println(text.length()); 4. What will be the value of text3 when the following code is executed? String text1 = a + b; String text2 = c; String text3 = text1 + text2 + text1; 2.4.3 Date and SimpleDateFormat The Date class is used to represent a time instance to a millisecond (one-thousandth of a second) precision. This class is in the java.util package. When a new Date object is created, it is set to the time it is created (the current time is determined by reading the time maintained by the operating system on your machine). The Date class includes the toString method that converts its internal format to a string repre- sentation, which we can use to display the time. For example, executing the code Date today; today = new Date( ); System.out.println(today.toString()); will display the current time in this format: Wed Jan 28 15:05:18 PDT 2006 Notice that the current time, when converted to a string format, includes the date information also. Internally, the time is kept as an elapsed time in milliseconds since the standard base time known as the epoch, which is January 1, 1970, 00:00:00 GMT (Greenwich Mean Time). 60 Chapter 2 Getting Started with Java Why is the class called Date when its purpose is to keep track of time? The reason is historical. In the older versions of Java, prior to JDK 1.1, the Date class was indeed used to manipulate the year, month, and day components of the current time. However, the way they are implemented was not amenable to internation- alization.With the newer versions of Java, we use the GregorianCalendar class for date manipulation.The GregorianCalendar class is explained in Chapter 3. If we do not like the default format, say we want to display only the month and year or only the hours and minutes in the AM/PM designation, then we can use the SimpleDateFormat class. This class is in the java.text package. For example, if we wu23399_ch02.qxd 12/12/06 17:26 Page 60
  • 88. want to display the month, day, and year in the MM/dd/yy shorthand format, such as 07/04/03, we write Date today; SimpleDateFormat sdf; today = new Date( ); sdf = new SimpleDateFormat(MM/dd/yy); System.out.println(sdf.format(today)); If today is June 28, 2006, the code will display the date as 06/28/06 Notice the format designation is done by passing the formatting string when a new SimpleDateFormat object is created. The letters in the formatting string are case- sensitive. The formatting string in this example must be MM/dd/yy, and the letters d and y must be in lowercase. By increasing the number of formatting letters, we can change the length of the information, say, 2006 instead of 06. In case of the month, we change it from the number to a name. For example, when we change sdf to sdf = new SimpleDateFormat(MMMM dd, yyyy); the dialog will display June 28, 2006 If we want to display which day of the week today is, we can use the letter E as in Date today; SimpleDateFormat sdf; today = new Date( ); sdf = new SimpleDateFormat(EEEE); System.out.println(Today is + sdf.format(today)); Table 2.1 lists the common letters used in the formatting for SimpleDate- Format. For more details, please consult the Java API documentation. 2.4 Sample Java Standard Classes 61 Table 2.1 is provided solely for the purpose of quick reference when you start using the class in real programs.Nobody expects you to remember all those sym- bols. What is important here is for you to grasp the key OOP concepts and the fundamental way in which objects and classes are used, not to memorize minute details that nobody remembers. wu23399_ch02.qxd 12/12/06 17:26 Page 61
  • 89. If you do not pass any string when creating a new SimpleDataFormat object, the default formatting is used. The sample Ch2DateDisplay class displays today’s date, using the default and programmer-designated format. 62 Chapter 2 Getting Started with Java Table Table 2.1 Some common formatting symbols for SimpleDateFormat and their meanings.Please check the Java API documentation for full details. Symbol Meaning Value Sample y Year Number yyyy → 2002 M Month in year Text or number MM → 10 MMM → Oct MMMM → October d Day in month Number dd → 20 D Day in year Number DDD → 289 h Hour in AM/PM Number hh → 09 H Hour in day (0–23) Number HH → 17 a AM/PM marker Text a → AM m Minutes in hour Number mm → 35 s Seconds in minute Number ss → 54 S Millisecond Number mmm → 897 E Day in week Text E → Sat EEEE → Saturday /* Chapter 2 Sample Program: Displays Formatted Date Information File: Ch2DateDisplay.java */ import java.util.*; //for Date import java.text.*; //for SimpleDateFormat class Ch2DateDisplay { public static void main( String[] args ) { Date today; SimpleDateFormat simpleDF1, simpleDF2; today = new Date(); wu23399_ch02.qxd 12/12/06 17:26 Page 62
  • 90. simpleDF1 = new SimpleDateFormat( ); simpleDF2 = new SimpleDateFormat (EEEE MMMM dd, yyyy); //Default short format display System.out.println(Today is + simpleDF1.format(today) ); //Programmer-designated long format display System.out.println(Today is + simpleDF2.format(today) ); } } 2.4 Sample Java Standard Classes 63 1. Write a code fragment to display today’s date in the 07-04-2002 format. 2. What will be displayed on the message dialog when the following code is executed if today is July 4, 1776? Date today; SimpleDateFormat sdf; today = new Date( ); sdf = new SimpleDateFormat(MMM dd, yyyy); System.out.println(Today is + sdf.format(today)); 2.4.4 Standard Input Analogous to System.out for output, we have System.in for input. We call the tech- nique to input data using System.in standard input. System.in accepts input from the keyboard. We also use the term console input to refer to standard input. Using System.in for input is slightly more complicated than using System.out for output. System.in is an instance of the InputStream class that provides only a facility to input 1 byte at a time with its read method. However, multiple bytes are required to represent common types of data such as strings. The Scanner class from the java.util package provides a necessary input facility to accommodate various input routines. We limit our discussion here to input of string values. We extend our discussion to input of numerical values in Chapter 3. To input data from the standard input by using a Scanner object, we first cre- ate it by passing System.in as follows: import java.util.*; ... Scanner scanner; scanner = new Scanner(System.in); System.in standard input console input Scanner wu23399_ch02.qxd 12/12/06 17:27 Page 63
  • 91. Once we have a Scanner object, then we can input a single word by using its next method. Here’s code to input the first name of a person: Scanner scanner = new Scanner(System.in); String firstName; //prompt the user for input System.out.print(Enter your first name: ); firstName = scanner.next( ); System.out.println(Nice to meet you, + firstName + .); The user interaction of this sample code is shown in Figure 2.16. In the dia- gram, the characters entered by the user are displayed in the console window as they are typed in, so the user can see what’s been entered. Printing out the values just en- tered is called echo printing. The string input is processed until the Enter (or Return) key is pressed, so we can erase the characters by pressing the Backspace key while entering the data. Now let’s consider the case in which we want to input both the first name and the last name. We can follow the sample code and input them one by one as follows: Scanner scanner = new Scanner(System.in); String firstName, lastName; System.out.print(Enter your first name: ); firstName = scanner.next( ); 64 Chapter 2 Getting Started with Java Figure 2.16 Sample interaction using System.in with Scanner and System.out. Keyboard input is echo- printed in the console window. Enter your first name: George ENTER Nice to meet you, George. System.out.print(Enter your first name: ); firstName = scanner.next( ); System.out.println(Nice to meet you, + firstName + .); This is entered by the user and echo printed by the system.To distinguish the input and output in the diagram,the input is displayed in a different color. This icon shows the pressing of the Enter (Return) key. Note: The console window used by your Java tool may or may not use a different color for echo printing. echo printing wu23399_ch02.qxd 1/12/07 10:26 Page 64
  • 92. Bad Version System.out.print(Enter your last name: ); lastName = scanner.next( ); System.out.println(Your name is + firstName + + lastName + .); Enter your first name: George Enter your last name: Washington Your name is George Washington. What can we do if we want input both the first name and the last name together as a single input? Consider the following (wrong) code: Scanner scanner = new Scanner(System.in); String fullName; System.out.print(Enter your first and last names: ); fullName = scanner.next( ); System.out.println(Your name is + fullName + .); Here’s a sample interaction of a user entering both the first name and the last name on a single line: Enter your first and last name: George Washington Your name is George. What happened to the last name? The blank space between the first name and the last name is treated as a delimiter. So the system has accepted the characters up to, but not including, the blank space as the input value. Because we know there are first and last names, we can input them individually as follows: Scanner scanner = new Scanner(System.in); String first, last; System.out.print(Enter your first and last name: ); first = scanner.next( ); last = scanner.next( ); System.out.println(Your name is + first + + last + .); Enter your first and last name: George Washington Your name is George Washington. Instead of treating each word individually, it is possible to enter a set of words as a single input. To do so, we must reset the delimiter to other than the blank space. Any character can be set as a delimiter, but since we want to input the whole line as a ENTER ENTER ENTER ENTER 2.4 Sample Java Standard Classes 65 wu23399_ch02.qxd 1/12/07 10:26 Page 65
  • 93. single input, it is most reasonable to set the delimiter to the Enter key. Here’s how we change the delimiter to the Enter key and accept the complete line as a single input: Scanner scanner = new Scanner(System.in); String lineSeparator = System.getProperty(line.separator); scanner.useDelimiter(lineSeparator); String quote; Note we’re using println here. System.out.println(Enter your favorite quote: ); quote = scanner.next( ); System.out.println(You entered: + quote); Enter your favorite quote: There never was a good war or a bad peace. You entered: There never was a good war or a bad peace. We override the default delimiter by calling the useDelimiter method and pass the appropriate argument. We use the class method getProperty of the System class to retrieve the actual sequence of characters for the Enter key that is specific to the platform which our program is running. For the Windows platform, for instance, we can call the useDelimiter method as scanner.useDelimiter(rn); But such code is guaranteed only to work on the Windows platform. It may or may not work on other platforms. To make the code general enough to work on all plat- forms, we use System.getProperty. Incidentally, the backslash character () is called a control character or an escape character. We’ll examine the use of control char- acters later in the book. ENTER 66 Chapter 2 Getting Started with Java 1. Write a code to input the last name of a user. 2. Show the content of the console window when the following code is executed and the text Barbaro is entered: Scanner scanner = new Scanner(System.in); String winner; System.out.print( Enter the name of the derby winner: ); winner = scanner.next( ); System.out.println(2006 Kentucky Derby Winner is + name + .); wu23399_ch02.qxd 12/12/06 17:27 Page 66
  • 94. 2.5 Sample Development 67 Printing the Initials Now that we have acquired a basic understanding of Java application programs, let’s write a new application.We will go through the design,coding,and testing phases of the software life cycle to illustrate the development process. Since the program we develop here is very simple,we can write it without really going through the phases.However,it is extremely important for you to get into a habit of developing a program by following the software life cycle stages. Small programs can be developed in a haphazard manner, but not large programs.We will teach you the development process with small programs first, so you will be ready to use it to create large programs later. We will develop this program by using an incremental development technique, which will develop the program in small incremental steps. We start out with a bare- bones program and gradually build up the program by adding more and more code to it. At each incremental step, we design, code, and test the program before moving on to the next step. This methodical development of a program allows us to focus our at- tention on a single task at each step, and this reduces the chance of introducing errors into the program. Problem Statement We start our development with a problem statement. The problem statement for our sample programs will be short,ranging from a sentence to a paragraph,but the problem statement for complex and advanced applications may contain many pages. Here’s the problem statement for this sample development exercise: Write an application that asks for the user’s first, middle, and last names and replies with the user’s initials. Overall Plan Our first task is to map out the overall plan for development.We will identify classes nec- essary for the program and the steps we will follow to implement the program.We begin with the outline of program logic.For a simple program such as this one,it is kind of obvi- ous; but to practice the incremental development, let’s put down the outline of program flow explicitly.We can express the program flow as having three tasks: 1. Get the user’s first,middle,and last names. 2. Extract the initials to formulate the monogram. 3. Output the monogram. Having identified the three major tasks of the program, we will now identify the classes we can use to implement the three tasks. First, we need an object to handle the input. At this point, we have learned about only the Scanner class, so we will use it here. Second, we need an object to display the result. Again, we will use System.out, as it is the only one we know at this point for displaying a string value. For the string Sample Development 2.5 Sample Development program tasks wu23399_ch02.qxd 12/12/06 17:27 Page 67
  • 95. 68 Chapter 2 Getting Started with Java 2.5 Sample Development—continued manipulation,we will use the String class.Finally,we will use these classes from the main class,which we will call Ch2Monogram.Let’s summarize these in a design document: Design Document: Monogram Class Purpose Ch2Monogram The main class of the program. Scanner The next method is used for getting the full name. String The class is used for string manipulation,extracting initials from the first,middle,and last names. (PrintStream) The standard output window is used for displaying the resulting System.out monogram. program classes The program diagram of Ch2Monogram is shown in Figure 2.17.Keep in mind that this is only a preliminary design.Although we are not going to see any changes made to this design document because this sample application is very simple, changes to the design document are expected as the programs we develop become larger and more complex.The preliminary document is really a working document that we will modify and expand as we progress through the development steps. Before we can actually start our development,we must sketch the steps we will fol- low to develop the program.There is more than one possible sequence of steps to develop Figure 2.17 The program diagram for Ch2Monogram. Ch2Monogram String PrintStream (System.out) Scanner wu23399_ch02.qxd 12/12/06 17:27 Page 68
  • 96. a program, and the number of possible sequences will increase as the program becomes more complex.For this program,we will develop the program in two steps: 1. Start with the program template and add code to get input. 2. Add code to compute and display the monogram. Step 1 Development: Getting Input The problem states that the program is to input the user’s name and display its initials. It does not specify how, so in the design stage, we will decide how to do this. Since, at this point, we know only one way to input data, that is, using the Scanner class, we will use it here.But in which form shall we input three pieces of data? There are two possible design alternatives. In the first design,we will input them separately: String firstName, middleName, lastName; Scanner scanner = new Scanner(System.in); System.out.print(First Name: ); firstName = scanner.next( ); System.out.print(Middle Name: ); middleName = scanner.next( ); System.out.print(Last Name: ); lastName = scanner.next( ); In the second design,we will input them together: String fullName; Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); System.out.print(Full Name: ); fullName = scanner.next( ); Which design is better? There is never“one correct answer”to the design problems. We have to select the one from the possible alternatives that satisfies the different crite- ria most effectively in a given situation. The criteria may include the user’s needs and preferences, faster performance, development costs, time contraints, and other factors. For example, in one situation, we may decide to forgo some great user interface features so the development can be completed under budget. In this sample development, we will consider the alternative designs from the overall quality of the program’s user interface. In other words, we want to make our pro- gram as user-friendly as possible.We want our users to have a pleasant experience using our program. The program should not be cumbersome to use, or else the users will get very frustrated in using the program. Which design would give the better user experi- ence? In the first approach,the user enters the information separately with three dialogs, 2.5 Sample Development 69 develop- ment steps step 1 design alternative design 1 alternative design 2 wu23399_ch02.qxd 12/12/06 19:29 Page 69
  • 97. 2.5 Sample Development—continued while in the second approach, the user enters the information together with one dialog. We choose the second approach because it allows quicker data entry,and in general,it is more natural to treat the name as a single entity than as three separate entitites. If we were to enter the name,address,and phone number,then we would use three dialogs as they are three separate entities. In this situation, we consider the first, middle, and last names as part of a single entity. Notice that the decision to enter the full name by using one dialog makes our task as the programmer slightly more difficult because we need to extract the first, middle, and last names from a single string. In the first approach, as we get the first, middle, and last names separately,there’s no such need.So,if we consider strictly the ease of develop- ment, the first approach is better. It is important to remember, however, that we are de- veloping the program for the sake of the users,not for ourselves. 70 Chapter 2 Getting Started with Java We develop programs for the sake of users,not for ourselves.Ease of use has higher priority than ease of development. Let’s implement the second design alternative. In the code, notice the use of the output statement that prints the string entered by the user.This printing of the input is another form of echo printing (introduced in Section 2.4.4). By echo printing, we verify that the input value is indeed read in correctly. /* Chapter 2 Sample Program: Displays the Monogram File: Step1/Ch2Monogram.java /* import java.util.*; class Ch2Monogram { public static void main(String[] args) { String name; Scanner scanner = new Scanner(System.in); step 1 code wu23399_ch02.qxd 12/12/06 17:27 Page 70
  • 98. scanner.useDelimiter(System.getProperty(line.separator)); System.out.print(Enter your full name (first, middle, last):); name = scanner.next( ); System.out.println(Name entered: + name); } } 2.5 Sample Development 71 After the program is written, we test the program to verify that the program runs as in- tended. The step 1 program may seem so trivial and not so useful,but it does serve a very useful purpose. Successful execution of this program verifies that the program setup is okay, the necessary packages are imported, and the objects are declared correctly. Since this program is very simple, there’s not much testing strategy we can employ other than simply running it.For subsequent sample programs,however,the testing strategy will be more involved.After the step 1 program is compiled and executed correctly,we move on to step 2. Step 2 Development: Computing and Displaying the Monogram The next task is to extract initials from the input string. First, because of our limited knowledge of programming at this point, we will assume the input is correct. That is, the input string contains first, middle, and last names, and they are separated by single blank spaces. Second, there are many possible solutions, but we will solve this problem by using only the methods covered in this chapter. Reviewing the string methods we covered in this chapter and the Ch2String Processing class, we know that a sequence of indexOf and substring methods can divide a string (full name) into two substrings (first and last names). How can we adapt this technique to now divide a string (full name) into three substrings (first, middle, and last names)? Aha! We apply the sequence one more time, as shown in Figure 2.18. Once we divide the input name into first,middle,and last names,extracting the ini- tials is a fairly straightforward application of the indexOf method.We can extract the first letter of a string as str.substring(0, 1) And the monogram can be formulated by concatenating three initials as first.substring(0, 1) + middle.substring(0, 1) + last.substring(0, 1) step 1 test step 2 design wu23399_ch02.qxd 12/12/06 17:27 Page 71
  • 99. 2.5 Sample Development—continued Here’s our step 2 code: 72 Chapter 2 Getting Started with Java Figure 2.18 Apply the two sequences of indexOf and substring methods to extract three substrings from a given string. General Idea String name; name = Jon Jay Java; Jon Jay Java Jon Jay Java Jay Java Actual Statements Jon Jay Java Jon Jay Java Jay Java first = name.substring(0, name.indexOf( )); name = name.substring(name.indexOf( )+1, name.length()); middle = name.substring(0, name.indexOf( )); last = name.substring(name.indexOf( )+1, name.length()); step 2 code /* Chapter 2 Sample Program: Displays the Monogram File: Step2/Ch2Monogram.java */ wu23399_ch02.qxd 12/12/06 17:27 Page 72
  • 100. import java.util.*; class Ch2Monogram { public static void main(String[] args) { String name; Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); System.out.print(Enter your full name (first, middle, last):); name = scanner.next( ); System.out.println(Name entered: + name); } } Summary 73 To verify the computation is working correctly, we run the program multiple times and enter different names. Remember that we are assuming there is no error in input; that is, first,middle,and last names are separated by single blank spaces.Since there are two sub- tasks involved in this step, it is important to test them separately.To verify that the input string is divided into three substrings correctly, we place the following temporary test output statements. System.out.println(First: + first); System.out.println(Middle: + middle); System.out.println(Last: + last); These statements are not shown in the step 2 program listing,but they are included in the actual sample code. step 2 test • The three basic components of a Java program are comments, import statements, and class declarations. • A Java program must have one class designated as the main class. The designated main class must have the main method. • An object must be declared and created before we can use it. • To command an object or a class to perform a task, we send a message to it. We use the expression calling a method synonymously with sending a message. • A single name can be used to refer to different objects (of the same class) at different times. An object with no reference will be returned to a system. S u m m a r y wu23399_ch02.qxd 12/12/06 17:27 Page 73
  • 101. • We follow the edit-compile-run cycle to execute programs. • A source file is compiled into a bytecode file by a Java compiler. • A Java interpreter (also called a Java Virtual Machine) executes the bytecode. • The standard classes introduced in this chapter are JFrame SimpleDateFormat Scanner String Date System.out System.in • Table 2.2 lists the Java naming convention. 74 Chapter 2 Getting Started with Java Table Table 2.2 Standard naming convention for Java Category Convention Example Class Instance Constant Package Use an uppercase letter for the first letter of the class names.If the name consists of multiple words,the first letter of every word is capitalized. Use a lowercase letter for the first letter of the object names.If the name consists of multiple words,the first letter of every word (except the first word) is capitalized. (Note:Sample use of a constant will appear in Chap.4.We include it here for completeness and easy reference later.) Use all uppercase letters.If the constant consists of multiple words,the underscore characters are used to separate the words. Use all lowercase letters. Customer MainWindow MyInputHandler customer inputHandler myFirstApplication DEFAULT_RATE DEG_TO_RAD CANCEL java game finance K e y C o n c e p t s standard classes program diagram identifier standard naming convention new operator garbage collection comments packages dot notation class declaration method declaration edit-compile-run cycle source file bytecode file wu23399_ch02.qxd 12/12/06 17:27 Page 74
  • 102. Exercises 75 E x e r c i s e s 1. Identify all errors in the following program (color highlighting is disabled): /* Program Exercise1 Attempting to display a frame window // import swing.JFrame; class Exercise 1 { public void Main() { JFrame frame; frame.setVisible(TRUE) } } 2. Identify all errors in the following program (color highlighting is disabled): // Program Exercise2 Attempting to display a frame of size 300 by 200 pixels // import Javax.Swing.*; class two { public static void main method() { myFrame JFrame; myFrame = new JFrame(); myFrame.setSize(300, 200); myFrame.setVisible(); } } 3. Identify all the errors in the following program (color highlighting is disabled): /* Program Exercise3 Attempting to display the number of characters in a given input. */ class three { public static void main( ) { String input; input = JOptionPane(input:); wu23399_ch02.qxd 12/12/06 17:27 Page 75
  • 103. System.out.print (Input has + input.length() + characters); } } 4. Describe the purpose of comments. Name the types of comments available. Can you include comment markers inside a comment? 5. What is the purpose of the import statement? Does a Java program always have to include an import statement? 6. Show the syntax for importing one class and all classes in a package. 7. Describe the class that must be included in any Java application. 8. What is a reserved word? List all the Java reserved words mentioned in this chapter. 9. Which of the following are invalid Java identifiers? a. R2D2 b. Whatchamacallit c. HowAboutThis? d. Java e. GoodChoice f. 12345 76 Chapter 2 Getting Started with Java g. 3CPO h. This is okay. i. thisIsReallyOkay j. DEFAULT_AMT k. Bad-Choice l. A12345 10. Describe the steps you take to run a Java application and the tools you use in each step. What are source files and bytecode files? What different types of errors are detected at each step? 11. Describe the difference between object declaration and object creation. Use a state-of-memory diagram to illustrate the difference. 12. Show a state-of-memory diagram after each of these statements is executed: JFrame window1; Resident res1, res2; window1 = new JFrame(); res1 = new Resident( ); res2 = new Resident( ); 13. Show a state-of-memory diagram after each of these statements is executed: Person person1, person2; person1 = new Person(); person2 = new Person(); person2 = new Person(); 14. Which of these identifiers violate the naming convention for class names? a. r2D2 b. whatchamacallit c. Java d. GoodName e. CPO f. ThisIsReallyOkay g. java h. badName wu23399_ch02.qxd 12/12/06 17:27 Page 76
  • 104. 15. Which of these identifiers violate the naming convention for object names? a. R2D2 b. isthisokay? c. Java d. goodName Exercises 77 e. 3CPO f. ThisIsReallyOkay g. java h. anotherbadone 16. For each of these expressions, determine its result. Assume the value of text is a string Java Programming. String text = Java Programming; a. text.substring(0, 4) b. text.length( ) c. text.substring(8, 12) d. text.substring(0, 1) + text.substring(7, 9) e. text.substring(5,6) + text.substring(text.length()-3, text.length()) 17. Write a Java application that displays today’s date in this format: Sunday November 10, 2002. 18. Write a Java application that displays a frame window 300 pixels wide and 200 pixels high with the title My First Frame. Place the frame so that its top left corner is at a position 50 pixels from the top of the screen and 100 pixels from the left of the screen. To position a window at a specified location, you use the setLocation method, as in //assume mainWindow is declared and created frame.setLocation( 50, 50 ); Through experimentation, determine how the two arguments in the setLocation method affect the positioning of the window. 19. Write a Java application that displays the two messages I Can Design and And I Can Program, using two separate dialogs. 20. Write a Java application that displays the two messages I Can Design and And I Can Program, using one dialog but in two separate lines. 21. Write a Java application that displays a very long message. Try a message that is wider than the display of your computer screen, and see what happens. 22. Because today’s computers are very fast, you will probably not notice any discernible difference on the screen between the code JFrame myWindow; myWindow = new JFrame( ); myWindow.setVisible( true ); wu23399_ch02.qxd 12/12/06 17:27 Page 77
  • 105. and JFrame myWindow; myWindow = new JFrame( ); myWindow.setVisible( true ); myWindow.setVisible( false ); myWindow.setVisible( true ); One way to see the disappearance and reappearance of the window is to put a delay between the successive setVisible messages. Here’s the magic code that puts a delay of 0.5 s: try {Thread.sleep(500);} catch(Exception e) { } The argument we pass to the sleep method specifies the amount of delay in milliseconds [note: 1000 milliseconds (ms) 1 second (s)]. We will not explain this magic code. 23. At the author’s website, you will find a Java package called galapagos. The galapagos package includes a Turtle class that is modeled after Seymour Papert’s logo. This Turtle has a pen, and when you move the Turtle, its pen will trace the movement. So by moving a Turtle object, you can draw many different kinds of geometric shapes. For example, this program commands a Turtle to draw a square: import galapagos.*; class Square { public static void main( String[] arg ) { Turtle turtle; turtle = new Turtle( ); turtle.move( 50 ); //move 50 pixels turtle.turn( 90 ); //turn 90 deg counterclockwise turtle.move( 50 ); turtle.turn( 90 ); turtle.move( 50 ); turtle.turn( 90 ); turtle.move( 50 ); } } Write a program to draw a triangle. Read the documentation and see if you can find a way to draw the square in a different color and line thickness. 24. Write a program to draw a star, using a Turtle from Exercise 23. 25. Write a program to draw a big letter J, using a Turtle from Exercise 23. 78 Chapter 2 Getting Started with Java wu23399_ch02.qxd 12/12/06 17:27 Page 78
  • 106. 26. Using a Turtle from Exercise 23, write a Java application that displays the text Hello as illustrated here: 27. Using a Turtle from Exercise 23 and employing the incremental development steps, build a Java application that draws a house. 28. Add the moon and a tree to the house you drew in Exercise 27. 29. Follow the incremental development methodology explained in this chapter to implement a program for the following problem statement. You must clearly write down the program tasks, create a design document with class descriptions, and draw the program diagram. Identify the development steps. State any assumptions you must make about the input. Articulate any design alternatives and justify your selection. Be sure to perform adequate testing at the end of each development step. Problem Statement: Write an application that asks the user for his or her birth date and replies with the day of the week on which he or she was born. We learned in this chapter that we can create a Date object for today’s date by writing import java.util.*; ... Date today = new Date(); Hello Hello Hello Hello Hello Exercises 79 wu23399_ch02.qxd 12/12/06 17:27 Page 79
  • 107. To create a Date object for a date other than today, we can use the Date class from the java.sql package. (A more general and flexible way to deal with a date by using the GregorianCalendar class is introduced in Chap. 3.) Notice that there are two distinct classes with the same name Date, but from different packages—one from java.util and another from java.sql. To distinguish the two, we will use the fully qualified names. To create a new java.util.Date object, we can call the class method valueOf of the java.sql.Date class with the string representation of a date. The string representation must be in the format yyyy-MM-dd. For example, to create a java.util.Date object for July 4, 1776, we write java.util.Date bdate = java.sql.Date.valueOf(1776-07-04); Notice that valueOf is a class method of the Date class in the java.sql package. Calling it with a correct argument will return a java.util.Date object for the specified date. 30. Repeat Exercise 29 for this problem statement: Problem Statement: Write an application that asks the user for her or his full name in the format first middle last and replies with the name in the format last , first middle-initial. where the last name is followed by comma and the middle initial is followed by period. For example, if the input is Decafe Chai Latte then the output is Latte, Decafe C. 80 Chapter 2 Getting Started with Java wu23399_ch02.qxd 12/12/06 17:27 Page 80
  • 108. Numerical Data O b j e c t i v e s After you have read and studied this chapter,you should be able to • Select proper types for numerical data. • Write arithmetic expressions in Java. • Evaluate arithmetic expressions,following the precedence rules. • Describe how the memory allocation works for objects and primitive data values. • Write mathematical expressions,using methods in the Math class. • Use the GregorianCalendar class in manipulating date information such as year, month,and day. • Use the DecimalFormat class to format numerical data. • Convert input string values to numerical data. • Input numerical data by using System.in and output numerical data by using System.out. • Apply the incremental development technique in writing programs. • (Optional) Describe how the integers and real numbers are represented in memory. 81 3 wu23399_ch03.qxd 12/13/06 17:38 Page 81
  • 109. 82 Chapter 3 Numerical Data I n t r o d u c t i o n hen we review the Ch2Monogram sample program, we can visualize three tasks: input, computation, and output. We view computer programs as getting input, per- forming computation on the input data, and outputting the results of the computa- tions. The type of computation we performed in Chapter 2 is string processing. In this chapter, we will study another type of computation, the one that deals with numerical data. Consider, for example, a metric converter program that accepts measurements in U.S. units (input), converts the measurements (computation), and displays their metric equivalents (output). The three tasks are not limited to numer- ical or string values, though. An input could be a mouse movement. A drawing pro- gram may accept mouse dragging (input), remember the points of mouse positions (computation), and draw lines connecting the points (output). Selecting a menu item is yet another form of input. For beginners, however, it is easiest to start writing programs that accept numerical or string values as input and display the result of computation as output. We will introduce more standard classes to reinforce the object-oriented style of programming. The Math class includes methods we can use to express mathe- matical formulas. The DecimalFormat class includes a method to format numerical data so we can display the data in a desired precision. The GregorianCalendar class includes methods to manipulate the date. In Chapter 2, we performed String input and output by using the standard input (Scanner) and output (System.out). We will describe the input and output routines for numerical data in this chapter. Finally, we will continue to employ the incremental development technique introduced in Chapter 2 in developing the sample application, a loan calculator pro- gram. As the sample program gets more complex, well-planned development steps will smooth the development effort. 3.1 Variables Suppose we want to compute the sum and difference of two numbers. Let’s call the two numbers x and y. In mathematics, we say x + y and x – y To compute the sum and the difference of x and y in a program, we must first declare what kind of data will be assigned to them. After we assign values to them, we can compute their sum and difference. Let’s say x and y are integers. To declare that the type of data assigned to them is an integer, we write int x, y; W wu23399_ch03.qxd 12/13/06 17:38 Page 82
  • 110. When this declaration is made, memory locations to store data values for x and y are allocated. These memory locations are called variables, and x and y are the names we associate with the memory locations. Any valid identifier can be used as a vari- able name. After the declaration is made, we can assign only integers to x and y. We cannot, for example, assign real numbers to them. 3.1 Variables 83 variable A variable has three properties:a memory location to store the value,the type of data stored in the memory location,and the name used to refer to the memory location. Although we must say “x and y are variable names” to be precise, we will use the abbreviated form “x and y are variables” or “x and y are integer variables” whenever appropriate. The general syntax for declaring variables is data type variables ; where variables is a sequence of identifiers separated by commas. Every variable we use in a program must be declared. We may have as many declarations as we wish. For example, we can declare x and y separately as int x; int y; However, we cannot declare the same variable more than once; therefore, the sec- ond declaration below is invalid because y is declared twice: int x, y, z; int y; There are six numerical data types in Java: byte, short, int, long, float, and double. The data types byte, short, int, and long are for integers; and the data types float and double are for real numbers. The data type names byte, short, and others are all reserved words. The difference among these six numerical data types is in the range of values they can represent, as shown in Table 3.1. A data type with a larger range of values is said to have a higher precision. For example, the data type double has a higher precision than the data type float. The tradeoff for higher precision is memory space—to store a number with higher pre- cision, you need more space. A variable of type short requires 2 bytes and a variable of type int requires 4 bytes, for example. If your program does not use many integers, then whether you declare them as short or int is really not that critical. The variable declaration syntax six numerical data types higher precision wu23399_ch03.qxd 12/13/06 17:38 Page 83
  • 111. difference in memory usage is very small and not a deciding factor in the program design. The storage difference becomes significant only when your program uses thousands of integers. Therefore, we will almost always use the data type int for in- tegers. We use long when we need to process very large integers that are outside the range of values int can represent. For real numbers, it is more common to use dou- ble. Although it requires more memory space than float, we prefer double because of its higher precision in representing real numbers. We will describe how the num- bers are stored in memory in Section 3.10. 84 Chapter 3 Numerical Data Table Table 3.1 Java numerical data types and their precisions † No default value is assigned to a local variable. A local variable is explained on page 184 in Section 4.8. ‡ The character E indicates a number is expressed in scientific notation. This notation is explained on page 96. Data Default Type Content Value† Minimum Value Maximum Value byte Integer 0 128 127 short Integer 0 32768 32767 int Integer 0 2147483648 2147483647 long Integer 0 9223372036854775808 9223372036854775807 float Real 0.0 3.40282347E+38‡ 3.40282347E+38 double Real 0.0 1.79769313486231570E+308 1.79769313486231570E+308 Application programs we develop in this book are intended for computers with a large amount of memory (such as desktops or laptops), so the storage space is not normally a major concern because we have more than enough. However, when we develop applications for embedded or specialized devices with a very limited amount of memory, such as PDAs, cellular phones, mobile robots for Mars exploration, and others, reducing the memory usage becomes a major concern. Here is an example of declaring variables of different data types: int i, j, k; float numberOne, numberTwo; long bigInteger; double bigNumber; At the time a variable is declared, it also can be initialized. For example, we may initialize the integer variables count and height to 10 and 34 as in int count = 10, height = 34; wu23399_ch03.qxd 12/13/06 17:38 Page 84
  • 112. We assign a value to a variable by using an assignment statement. To assign the value 234 to the variable named firstNumber, for example, we write firstNumber = 234; Be careful not to confuse mathematical equality and assignment. For example, the following are not valid Java code: 4 + 5 = x; x + y = y + x; The syntax for the assignment statement is variable = expression ; where expression is an arithmetic expression, and the value of expression is assigned to the variable. The following are sample assignment statements: sum = firstNumber + secondNumber; solution = x * x - 2 * x + 1; average = (x + y + z) / 3.0; We will present a detailed discussion of arithmetic expressions in Section 3.2. One key point we need to remember about variables is the following: 3.1 Variables 85 As we mentioned in Chapter 2, you can declare and create an object just as you can initialize variables at the time you declare them. For example, the declaration Date today = new Date(); is equivalent to Date today; today = new Date(); assignment statement syntax assignment statement Before using a variable,first we must declare and assign a value to it. The diagram in Figure 3.1 illustrates the effect of variable declaration and as- signment. Notice the similarity with this and memory allocation for object declara- tion and creation, illustrated in Figure 2.4 on page 36. Figure 3.2 compares the two. wu23399_ch03.qxd 12/13/06 17:38 Page 85
  • 113. What we have been calling object names are really variables. The only difference between a variable for numbers and a variable for objects is the contents in the memory locations. For numbers, a variable contains the numerical value itself; and for objects, a variable contains an address where the object is stored. We use an arrow in the diagram to indicate that the content is an address, not the value itself. 86 Chapter 3 Numerical Data State of Memory int firstNumber, secondNumber; firstNumber = 234; secondNumber = 87; firstNumber = 234; secondNumber = 87; The variables firstNumber and secondNumber are declared and set in memory. A B int firstNumber, secondNumber; after is executed A firstNumber secondNumber Values are assigned to the variables firstNumber and secondNumber. after is executed B firstNumber secondNumber 234 87 Figure 3.1 A diagram showing how two memory locations (variables) with names firstNumber and secondNumber are declared,and values are assigned to them. Object names are synonymous with variables whose contents are references to objects (i.e.,memory addresses). Figure 3.3 contrasts the effect of assigning the content of one variable to an- other variable for numerical data values and for objects. Because the content of a variable for objects is an address, assigning the content of a variable to another makes two variables that refer to the same object. Assignment does not create a new object. Without executing the new command, no new object is created. We can view the situation in which two variables refer to the same object as the object having two distinct names. wu23399_ch03.qxd 12/13/06 17:38 Page 86
  • 114. For numbers, the amount of memory space required is fixed. The values for data type int require 4 bytes, for example, and this won’t change. However, with ob- jects, the amount of memory space required is not constant. One instance of the Account class may require 120 bytes, while another instance of the same class may require 140 bytes. The difference in space usage for the account objects would occur if we had to keep track of checks written against the accounts. If one account has 15 checks written and the second account has 25 checks written, then we need more memory space for the second account than for the first account. We use the new command to actually create an object. Remember that declar- ing an object only allocates the variable whose content will be an address. On the 3.1 Variables 87 customer = new Customer(); customer = new Customer(); number = 237; number = 35; Numerical Data Object int number; Customer customer; int number; number = 35; number = 237; Customer customer; customer = new Customer(); customer = new Customer(); number customer customer customer number 237 number 35 :Customer :Customer :Customer int number; number = 237; number = 35; Customer customer; customer = new Customer(); customer = new Customer(); Figure 3.2 A difference between object declaration and numerical data declaration. wu23399_ch03.qxd 12/13/06 17:38 Page 87
  • 115. 88 Chapter 3 Numerical Data Numerical Data Object number1 = 237; number2 = number1; int number1, number2; alan = new Professor(); turing = alan; Professor alan, turing; number2 number1 turing alan number2 number1 turing alan number1 = 237; int number1, number2; alan = new Professor(); Professor alan, turing; number2 = number1; turing = alan; :Professor :Professor number2 number1 turing alan number1 = 237; int number1, number2; alan = new Professor(); Professor alan, turing; number2 = number1; turing = alan; 237 237 237 Figure 3.3 An effect of assigning the content of one variable to another. other hand, we don’t “create” an integer because the space to store the value is already allocated at the time the integer variable is declared. Because the contents are addresses that refer to memory locations where the objects are actually stored, objects are called reference data types. In contrast, numerical data types are called primitive data types. reference versus primitive data types wu23399_ch03.qxd 12/13/06 17:38 Page 88
  • 116. 3.1 Variables 89 In addition to the six numerical data types, there are two nonnumerical primitive data types. The data type boolean is used to represent two logical values true and false. For example,the statements boolean raining; raining = true; assign the value true to a boolean variable raining. We will explain and start using boolean variables beginning in Chapter 5. The second nonnumerical primitive data type is char (for character). It is used to represent a single character (letter, digit, punctuation marks, and others). The following example assigns the upper- case letter A to a char variable letter: char letter; letter = 'A'; A char constant is designated by single quotes. We will study the char data type in Chapter 9 on string processing. 1. Why are the following declarations all invalid (color highlighting is disabled)? int a, b, a; float x, int; float w, int x; bigNumber double; 2. Assuming the following declarations are executed in sequence, why are the second and third declarations invalid? int a, b; int a; float b; 3. Name six data types for numerical values. 4. Which of the following are valid assignment statements (assuming the variables are properly declared)? x = 12; 12 = x; y + y = x; y = x + 12; 5. Draw the state-of-memory diagram for the following code. Account latteAcct, espressoAcct; latteAcct = new Account(); espressoAcct = new Account(); latteAcct = espressoAcct; wu23399_ch03.qxd 12/13/06 17:38 Page 89
  • 117. 3.2 Arithmetic Expressions An expression involving numerical values such as 23 + 45 is called an arithmetic expression, because it consists of arithmetic operators and operands. An arithmetic operator, such as + in the example, designates numerical computation. Table 3.2 summarizes the arithmetic operators available in Java. Notice how the division operator works in Java. When both numbers are inte- gers, the result is an integer quotient. That is, any fractional part is truncated. Divi- sion between two integers is called integer division. When either or both numbers are float or double, the result is a real number. Here are some division examples: 90 Chapter 3 Numerical Data arithmetic operator integerdivision Table Table 3.2 Arithmetic operators Java Value Operation Operator Example (x 10,y 7,z 2.5) Addition + x + y 17 Subtraction – x – y 3 Multiplication * x * y 70 Division / x / y 1 x / z 4.0 Modulo division % x % y 3 (remainder) Division Operation Result 23 / 5 4 23 / 5.0 4.6 25.0 / 5.0 5.0 The modulo operator returns the remainder of a division. Although real num- bers can be used with the modulo operator, the most common use of the modulo operator involves only integers. Here are some examples: Modulo Operation Result 23 % 5 3 23 % 25 23 16 % 2 0 wu23399_ch03.qxd 12/13/06 17:38 Page 90
  • 118. The expression 23 % 5 results in 3 because 23 divided by 5 is 4 with remainder 3. Notice that x % y = 0 when y divides x perfectly; for example, 16 % 2 = 0. Also notice that x % y = x when y is larger than x; for example, 23 % 25 = 23. An operand in arithmetic expressions can be a constant, a variable, a method call, or another arithmetic expression, possibly surrounded by parentheses. Let’s look at examples. In the expression x + 4 we have one addition operator and two operands—a variable x and a constant 4. The addition operator is called a binary operator because it operates on two operands. All other arithmetic operators except the minus are also binary. The minus and plus operators can be both binary and unary. A unary operator operates on one operand as in –x In the expression x + 3 * y the addition operator acts on operands x and 3 * y. The right operand for the addition operator is itself an expression. Often a nested expression is called a subexpression. The subexpression 3 * y has operands 3 and y. The following diagram illustrates this relationship: When two or more operators are present in an expression, we determine the order of evaluation by following the precedence rules. For example, multi- plication has a higher precedence than addition. Therefore, in the expression x + 3 * y, the multiplication operation is evaluated first, and the addition operation is evaluated next. Table 3.3 summarizes the precedence rules for arithmetic operators. y 3 x 3.2 Arithmetic Expressions 91 operand binary operator subexpression precedence rules wu23399_ch03.qxd 12/13/06 17:38 Page 91
  • 119. The following example illustrates the precedence rules applied to a complex arithmetic expression: 92 Chapter 3 Numerical Data Table Table 3.3 Precedence rules for arithmetic operators and parentheses Order Group Operator Rule High Subexpression ( ) Subexpressions are evaluated first.If parentheses are nested,the innermost subexpression is evaluated first.If two or more pairs of parentheses are on the same level,then they are evaluated from left to right. Unary operator -, + Unary minuses and pluses are evaluated second. Multiplicative *, /, % Multiplicative operators are evaluated operator third.If two or more multiplicative operators are in an expression,then they are evaluated from left to right. Low Additive operator +, - Additive operators are evaluated last.If two or more additive operators are in an expression,then they are evaluated from left to right. a * (b + -(c / d) / e) * (f - g % h) 1 5 6 2 3 4 7 8 When an arithmetic expression consists of variables and constants of the same data type, then the result of the expression will be that data type also. For example, if the data type of a and b is int, then the result of the expression a * b + 23 is also an int. When the data types of variables and constants in an arithmetic ex- pression are different data types, then a casting conversion will take place. A casting conversion, or typecasting, is a process that converts a value of one data type to an- other data type. Two types of casting conversions in Java are implicit and explicit. implicit and explicit type- casting wu23399_ch03.qxd 12/13/06 17:38 Page 92
  • 120. An implicit conversion called numeric promotion is applied to the operands of an arithmetic operator. The promotion is based on the rules stated in Table 3.4. This conversion is called promotion because the operand is converted from a lower to a higher precision. Instead of relying on implicit conversion, we can use explicit conversion to convert an operand from one data type to another. Explicit conversion is applied to an operand by using a typecast operator. For example, to convert the int variable x in the expression x / 3 to float so the result will not be truncated, we apply the typecast operator (float) as (float) x / 3 The syntax is ( data type ) expression The typecast operator is a unary operator and has a precedence higher than that of any binary operator. You must use parentheses to typecast a subexpression; for ex- ample, the expression a + (double) (x + y * z) will result in the subexpression x + y * z typecast to double. Assuming the variable x is an int, then the assignment statement x = 2 * (14343 / 2344); will assign the integer result of the expression to the variable x. However, if the data type of x is other than int, then an implicit conversion will occur so that the 3.2 Arithmetic Expressions 93 Table Table 3.4 Rules for arithmetic promotion Operator Type Promotion Rule Unary 1. If the operand is of type byte or short,then it is converted to int. 2. Otherwise,the operand remains the same type. Binary 1. If either operand is of type double,then the other operand is converted to double. 2. Otherwise,if either operand is of type float,then the other operand is converted to float. 3. Otherwise,if either operand is of type long,then the other operand is converted to long. 4. Otherwise,both operands are converted to int. numeric promotion typecast operator typecasting syntax wu23399_ch03.qxd 12/13/06 17:38 Page 93
  • 121. data type of the expression becomes the same as the data type of the variable. An assignment conversion is another implicit conversion that occurs when the vari- able and the value of an expression in an assignment statement are not of the same data type. An assignment conversion occurs only if the data type of the variable has a higher precision than the data type of the expression’s value. For example, double number; number = 25; is valid, but int number; number = 234.56; INVALID is not. In writing programs, we often have to increment or decrement the value of a variable by a certain amount. For example, to increase the value of sum by 5, we write sum = sum + 5; We can rewrite this statement witout repeating the same variable on the left- and right-hand sides of the assignment symbol by using the shorthand assignment operator: sum += number; Table 3.5 lists five shorthand assignment operators available in Java. These shorthand assignment operators have precedence lower than that of any other arithmetic operators; so, for example, the statement sum *= a + b; is equivalent to sum = sum * (a + b); 94 Chapter 3 Numerical Data assignment conversion shorthand assignment operator Table Table 3.5 Shorthand assignment operators Operator Usage Meaning += a += b; a = a + b; -= a -= b; a = a - b; *= a *= b; a = a * b; /= a /= b; a = a / b; %= a %= b; a = a % b; wu23399_ch03.qxd 12/13/06 17:38 Page 94
  • 122. 3.3 Constants 95 If we wish to assign a value to multiple variables, we can cascade the assignment operations as x = y = 1; which is equivalent to saying y = 1; x = 1; The assignment symbol = is actually an operator, and its precedence order is lower than that of any other operators. Assignment operators are evaluated right to left. 1. Evaluate the following expressions. a. 3 + 5 / 7 b. 3 * 3 + 3 % 2 c. 3 + 2 / 5 + -2 * 4 d. 2 * (1 + -(3/4) / 2) * (2 - 6 % 3) 2. What is the data type of the result of the following expressions? a. (3 + 5) / 7 b. (3 + 5) / (float) 7 c. (float) ( (3 + 5) / 7 ) 3. Which of the following expressions is equivalent to b(c 34)(2a)? a. -b * (c + 34) / 2 * a b. -b * (c + 34) / (2 * a) c. -b * c + 34 / (2 * a) 4. Rewrite the following statements without using the shorthand operators. a. x += y; b. x *= v + w; c. x /= y; 3.3 Constants While a program is running, different values may be assigned to a variable at dif- ferent times (thus the name variable, since the values it contains can vary), but in some cases we do not want this to happen. In other words, we want to “lock” the assigned value so that no changes can take place. If we want a value to remain fixed, then we use a constant. A constant is declared in a manner similar to a variable but constant wu23399_ch03.qxd 12/13/06 17:38 Page 95
  • 123. with the additional reserved word final. A constant must be assigned a value at the time of its declaration. Here’s an example of declaring four constants: final double PI = 3.14159; final short FARADAY_CONSTANT = 23060; // unit is cal/volt final double CM_PER_INCH = 2.54; final int MONTHS_IN_YEAR = 12; We follow the standard Java convention to name a constant, using only capi- tal letters and underscores. Judicious use of constants makes programs more read- able. You will be seeing many uses of constants later in the book, beginning with the sample program in this chapter. The constant PI is called a named constant or symbolic constant. We refer to symbolic constants with identifiers such as PI and FARADAY_CONSTANT. The sec- ond type of constant is called a literal constant, and we refer to it by using an actual value. For example, the following statements contain three literal constants: final double PI = 3.14159 ; double area; area = 2 * PI * 345.79 ; When we use the literal constant 2, the data type of the constant is set to int by default. Then how can we specify a literal constant of type long?1 We append the constant with an l (a lowercase letter L) or L as in 2L * PI * 345.79 How about the literal constant 345.79? Since the literal constant contains a decimal point, its data type can be only float or double. But which one? The answer is double. If a literal constant contains a decimal point, then it is of type double by default. To designate a literal constant of type float, we must append the letter f or F. For example, 2 * PI * 345.79F To represent a double literal constant, we may optionally append a d or D. So the following two constants are equivalent: 2 * PI * 345.79 is equivalent to 2 * PI * 345.79D We also can express float and double literal constants in scientific notation as Number 10exponent 96 Chapter 3 Numerical Data named constant literal constant Literal constants 1 In most cases, it is not significant to distinguish the two because of automatic type conversion; see Section 3.2. wu23399_ch03.qxd 12/13/06 17:38 Page 96
  • 124. which in Java is expressed as number E exponent 3.4 Displaying Numerical Values 97 exponential notation in Java Since a numerical constant such as 345.79 represents a double value, these statements float number; number = 345.79; for example, would result in a compilation error. The data types do not match, and the variable (float) has lower precision than that of the constant (double). To correct this error,we have to write the assignment statement as number = 345.79f; or number = (float) 345.79; This is one of the common errors that people make in writing Java programs, es- pecially those with prior programming experience. where number is a literal constant that may or may not contain a decimal point and exponent is a signed or an unsigned integer. Lowercase e may be substituted for the exponent symbol E. The whole expression may be suffixed by f, F, d, or D. The number itself cannot be suffixed with symbols f, F, d, or D. Here are some examples: 12.40e+209 23E33 29.0098e–102 234e+5D 4.45e2 Here are some additional examples of constant declarations: final double SPEED_OF_LIGHT = 3.0E+10D; // unit is cm/s final short MAX_WGT_ALLOWED = 400; 3.4 Displaying Numerical Values In Chapter 2, we learned how to output string values to the console window by using System.out. We can easily output numerical values to the console window as well. We will use the same print and println methods to output wu23399_ch03.qxd 1/11/07 11:49 Page 97
  • 125. numerical values. Here’s a simple example that outputs the values of a constant and a variable: int num = 15; System.out.print(num); //print a variable System.out.print( ); //print a blank space System.out.print(10); //print a constant Executing the code will result in the following console window: We can use the println method to skip a line after printing out the value. Executing int num = 15; System.out.println(num); System.out.println(10); will result in By using the concatenation operation, it is possible to output multiple values with a single print or println method. For example, the statement System.out.print(30 + + 40); is equivalent to System.out.print(30); System.out.print( ); System.out.print(40); Notice that the expression 30 + + 40 mixes numerical values and a string. We learned in Chapter 2 that the plus symbol is used to concatenate strings, for example, Benjamin + + Franklin 98 Chapter 3 Numerical Data 15 10 Console Window 15 10 Console Window wu23399_ch03.qxd 12/13/06 17:38 Page 98
  • 126. And, in this chapter, we learned the same plus symbol is used to add numerical values, for example, 4 + 36 The plus symbol, therefore, could mean two different things: string concatenation or numerical addition. When a symbol is used to represent more than one operation, this is called operator overloading. What happens when the plus symbol appears in a mixed expression? When the Java compiler encounters an overloaded operator, the compiler determines the mean- ing of a symbol by its context. If the left operand and the right operand of the plus symbol are both numerical values, then the compiler will treat the symbol as addition; otherwise, it will treat the symbol as concatenation.The plus symbol operator is eval- uated from left to right, and the result of concatenation is a string, so the code int x = 1; int y = 2; String output = test + x + y; will result in output being set to test12 while the statement String output = x + y + test; will result in output being set to 3test To get the result of test3, we have to write the statement as String output = test + (x + y); so the arithmetic addition is performed first. Now let’s look at a small sample program that illustrates a typical use of string concatenation in displaying computation results. In this sample program, we compute the circumference and the area of a circle with a given radius. The value for the radius is assigned in the program (we will discuss how to input this value in Section 3.5). Here’s the program: 3.4 Displaying Numerical Values 99 operator overloading /* Chapter 3 Sample Program: Compute Area and Circumference File: Ch3Circle.java */ test 1 test1 test12 2 1 2 3 3test test (add) wu23399_ch03.qxd 12/13/06 17:38 Page 99
  • 127. When we run this program, we get the following output: Notice the precision of decimal places displayed for the results, especially the one for the circumference. Although we desire such a high level of precision provided by double values during the computation, we may not when displaying the result. We can restrict the number of decimal places to display by using the DecimalFormat class from the java.text package. Although the full use of the DecimalFormat class can be fairly complicated, it is very straightforward if all we want is to limit the number of decimal places to be displayed. To limit the decimal places to three, for example, we create a DecimalFormat object as DecimalFormat df = new DecimalFormat(0.000); and use it to format the number as double num = 234.5698709; System.out.println(Num: + df.format(num)); When we add an instance of the DecimalFormat class named df and change the output statement of the Ch3Circle class to System.out.println(Given Radius: + df.format(radius)); System.out.println(Area: + df.format(area)); System.out.println(Circumference: + df.format(circumference)); 100 Chapter 3 Numerical Data class Ch3Circle { public static void main(String[] args) { final double PI = 3.14159; double radius, area, circumference; radius = 2.35; //compute the area and circumference area = PI * radius * radius; circumference = 2.0 * PI * radius; System.out.println(Given Radius: + radius); System.out.println(Area: + area); System.out.println(Circumference: + circumference); } } Given Radius: 2.35 Area: 17.349430775000002 Circumference: 14.765473 Console Window wu23399_ch03.qxd 12/13/06 17:38 Page 100
  • 128. we produce the following result: The modified class is named Ch3Circle2 (not shown here). Instead of using one println method per line of output, it is possible to output multiple lines with a single println or print method by embedding a new-line control character in the output. We briefly mentioned a control character in Section 2.4.4. A control character is for controlling the output, and we use the backslash symbol to denote a control character. The new-line control character is denoted as n and has the effect of pressing the Enter key in the output. For example, the statements System.out.println(Given Radius: + radius); System.out.println(Area: + area); System.out.println(Circumference: + circumference); can be written by using only one println statement as System.out.println(Given Radius: + radius + n + Area: + area + n + Circumference: + circumference); There is no limit to the number of new-line control characters you can embed, so we can easily skip two lines, for example, by putting two new-line control characters as follows: System.out.println(Number 1: + num1 + nn + Number 2: + num2); Another useful control character is a tab, which is denoted as t. We can use the tab control character to output the labels, and this results in two columns as follows: System.out.println(Given Radius: + t + radius + n + Area: + tt + area + n + Circumference: + t + circumference); Notice there are two tabs before we output the area. You need to experiment with the actual number of tabs to get the right output (the actual number of spaces used for each tab could be different depending on your Java IDE). The resulting output will be 3.4 Displaying Numerical Values 101 Given Radius: 2.350 Area: 17.349 Circumference: 14.765 Console Window new-line control character tab control character Given Radius: 2.35 Area: 17.349430775000002 Circumference: 14.765473 Console Window wu23399_ch03.qxd 12/13/06 17:38 Page 101
  • 129. You can also adjust the output format by appending blank spaces in the label. For example, you can rewrite the sample statement as System.out.println(Given Radius: + t + radius + n + Area: + t + area + n + Circumference: + t + circumference); And, as always, the use of symbolic constants will clean up the code: ... final String TAB = t; final String NEWLINE = n; ... System.out.println( Given Radius: + TAB + radius + NEWLINE + Area: + TAB + area + NEWLINE + Circumference: + TAB + circumference); The new program that illustrates the use of both DecimalFormat and control char- acters is named Ch3Circle3. Here’s the program: 102 Chapter 3 Numerical Data /* Chapter 3 Sample Program: Compute Area and Circumference File: Ch3Circle3.java */ import java.text.*; class Ch3Circle3 { public static void main(String[] args) { final double PI = 3.14159; final String TAB = t; final String NEWLINE = n; double radius, area, circumference; DecimalFormat df = new DecimalFormat(0.000); radius = 2.35; //compute the area and circumference area = PI * radius * radius; circumference = 2.0 * PI * radius; //Display the results System.out.println( Given Radius: + TAB + df.format(radius) + NEWLINE + wu23399_ch03.qxd 12/13/06 17:38 Page 102
  • 130. 3.5 Getting Numerical Input 103 1. What is the purpose of the control characters? 2. Which control character is used for a new line? 3. Using one print statement, output the following: Hello, world! My favorite Ben Franklin quote: An investment in knowledge always pays the best interest. 3.5 Getting Numerical Input We learned how to input string values by using the Scanner class in Chapter 2. We study how to input numerical values with the Scanner class in this section. To input strings, we use the next method of the Scanner class. For the numerical input values, we use an equivalent method that corresponds to the data type of the value we try to input. For instance, to input an int value, we use the nextInt method. Here’s an example of inputting a person’s age: Scanner scanner = new Scanner(System.in); int age; System.out.print(Enter your age: ); age = scanner.nextInt( ); In addition to the int data type, we have five input methods that correspond to the other numerical data types. The six input methods for the primitive numerical data types are listed in Table 3.6. Table Table 3.6 Methods to input six numerical data types Method Example nextByte( ) byte b = scanner.nextByte( ); nextDouble( ) double d = scanner.nextDouble( ); nextFloat( ) float f = scanner.nextFloat( ); nextInt( ) int i = scanner.nextInt( ); nextLong( ) long l = scanner.nextLong( ); nextShort( ) short s = scanner.nextShort( ); Area: + TAB + df.format(area) + NEWLINE + Circumference: + TAB + df.format(circumference)); } } wu23399_ch03.qxd 12/13/06 17:38 Page 103
  • 131. ENTER ENTER Enter two integers: 12 87 num1 = 12 and num2 = 87 Since the new-line character (when we press the Enter key, this new-line char- acter is entered into the system) is also treated as white space, we can enter the two integers by pressing the Enter key after each number. Here’s a sample: 104 Chapter 3 Numerical Data The following example inputs a person’s height in inches (int) and GPA (float): Scanner scanner = new Scanner(System.in); int height; float gpa; System.out.print(Enter your height in inches: ); height = scanner.nextInt( ); System.out.print(Enter your gpa: ); gpa = scanner.nextFloat( ); Remember that the default delimiter between the input values is a white space (such as the blank space or a tab); it is possible to input more than one value on a single line. The following code inputs two integers: Scanner scanner = new Scanner(System.in); int num1, num2; System.out.print(Enter two integers: ); num1 = scanner.nextInt( ); num2 = scanner.nextInt( ); System.out.print(num1 = + num1 + num2 = + num2); And here’s a sample interaction: ENTER When we enter data using System.in, they are placed in input buffer. And the next available data in the input buffer are processed when one of the input methods is called. This means that the actual processing of input data does not necessarily correspond to the display timing of the prompts. Let’s look at an example. Consider the following code: Scanner scanner = new Scanner(System.in); int num1, num2, num3; Space separates the two input values. input buffer Enter two integers: 12 8 num1 = 12 and num2 = 87 wu23399_ch03.qxd 12/13/06 17:38 Page 104
  • 132. 3.5 Getting Numerical Input 105 System.out.print(Enter Number 1: ); num1 = scanner.nextInt( ); System.out.print(Enter Number 2: ); num2 = scanner.nextInt( ); System.out.print(Enter Number 3: ); num3 = scanner.nextInt( ); System.out.print(Values entered are + num1 + + num2 + + num3); We expect the majority of users will input three integers, one at a time, as requested by the prompts: Enter Number 1: 10 Enter Number 2: 20 Enter Number 3: 30 Values entered are 10 20 30 However, users do not really have to enter the values one at a time. It is possible to enter all three values on a single line without waiting for prompts, for example. This will result in an awkward display in the console window. Here’s an example: Enter Number 1: 10, 20, 30 Enter Number 2: Enter Number 3: Values entered are 10 20 30 Although the display is awkward, the input values are assigned to the respec- tive variables correctly. This is so because the three input values are placed in the input buffer, and when the second and third nextInt methods are called, the corre- sponding values are in the input buffer, so there’s no problem inputting them. In Section 3.2, we explained the assignment conversion that allows us to assign a value to a higher-precision variable (e.g., assigning an int value to a dou- ble variable). This type of implicit conversion also occurs with the Scanner class. For example, the nextDouble method works without a problem as long as the user enters a value that is assignable to a double variable. Here’s an example: Scanner scanner = new Scanner(System.in); double num; System.out.print(Enter a double: ); num = scanner.nextDouble( ); System.out.print(You entered + num); Enter a double: 35 You entered 35.0 ENTER ENTER ENTER ENTER ENTER wu23399_ch03.qxd 12/13/06 17:38 Page 105
  • 133. Bad Version ENTER ENTER ENTER Everything seems to be working okay. What will happen if the name of a horse has more than one word, such as Sea Biscuit? The code will not work because only the first word is assigned to the String variable horseName. Remember that the default delimiter is the white space, so the blank space after the first word is treated as the end of the first input. Here’s the result when you enter Sea Biscuit: 106 Chapter 3 Numerical Data Enter the horse name: Sea Biscuit Enter the age: java.util.InputMismatchException at java.util.Scanner.throwFor(Scanner.java:819) at java.util.Scanner.next(Scanner.java:1431) at java.util.Scanner.nextInt(Scanner.java:2040) ... Only the first four lines of error messages are shown here. The nextDouble method accepts the value 35 and then converts it to a double data type. The method returns a double value, so even if the user enters an integer, you cannot assign the input to an int variable. The following code is therefore invalid: Scanner scanner = new Scanner(System.in); int num; System.out.print(Enter an integer: ); num = scanner.nextDouble( ); Type mismatch System.out.print(You entered + num); Now let’s study how we can mix the input of strings and numerical values. We begin with an example. Consider inputting a racehorse’s name and age. Here are a proposed code and a sample of expected interaction: Scanner scanner = new Scanner(System.in); String horseName; int age; System.out.print(Enter the horse name: ); horseName = scanner.next( ); System.out.print(Enter the age: ); age = scanner.nextInt( ); System.out.print(horseName + is + age + years old. ); Enter the horse name: Barbaro Enter the age: 3 Barbaro is 3 years old. wu23399_ch03.qxd 12/13/06 17:38 Page 106
  • 134. ENTER ENTER 3.5 Getting Numerical Input 107 To input more than one string and primitive numerical data,set the line separator as the delimiter and input one value per input line. The most reasonable solution here is to change the delimiter to the line sepa- rator, as described in Section 2.4.4. Here’s how: Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); //the rest is the same Enter the horse name: Sea Biscuit Enter the age: 3 Sea Biscuit is 3 years old. For most situations, using the line separator as the delimiter and inputting one value per input line are the best approach. We can, however, use any string for the delimiter. So, for example, we can delimit the input values with a character such as the pound sign (#), provided, of course, that the pound sign does not occur in the actual input values. Instead of using the data type specific methods such as nextInt, nextDouble, and others of the Scanner class, we can input a numerical value in a string format and convert it to an appropriate data type by ourselves. For example, we can use the class method parseInt of the Integer class to convert a string to an int. Here’s a state- ment that converts 14 to an int value 14: int num = Integer.parseInt(14); So, the statement int num = Integer.parseInt(scanner.next( )); is equivalent to int num = scanner.nextInt( ); Passing a string that cannot be converted to an int (e.g., 12b) will result in an error. The conversion method is not particularly useful or necessary with the scanner, but it can be when the input source is different from the scanner. Other common conversion methods are parseDouble, parseFloat, and parseLong of the Double, Float, and Long classes, respectively. wu23399_ch03.qxd 12/13/06 17:38 Page 107
  • 135. 108 Chapter 3 Numerical Data /* Chapter 3 Sample Program: Compute Area and Circumference with formatting and standard I/O File: Ch3Circle4.java */ import java.text.*; import java.util.*; class Ch3Circle4 { public static void main(String[] args) { final double PI = 3.14159; final String TAB = t; final String NEWLINE = n; double radius, area, circumference; Scanner scanner = new Scanner(System.in); DecimalFormat df = new DecimalFormat(0.000); System.out.println(Enter radius: ); radius = scanner.nextDouble( ); //compute the area and circumference area = PI * radius * radius; circumference = 2.0 * PI * radius; //Display the results System.out.println( Given Radius: + TAB + df.format(radius) + NEWLINE + Area: + TAB + df.format(area) + NEWLINE + Circumference: + TAB + df.format(circumference)); } } 1. Write a code to input the height of a user in feet (int) and inches (int). 2. Write a code to input the full name of a person and his or her age. The full name of a person includes the first name and the last name. 3. Write a code that creates a Scanner object and sets its delimiter to the pound sign. We close this section by presenting a sample program that extends the Ch3Circle3 class by accepting the radius of a circle as an input. Here’s the program: wu23399_ch03.qxd 12/13/06 17:38 Page 108
  • 136. 3.6 The Math Class Using only the arithmetic operators to express numerical computations is very lim- iting. Many computations require the use of mathematical functions. For example, to express the mathematical formula 1 2 sin x y we need the trigonometric sine and square root functions. The Math class in the java.lang package contains class methods for commonly used mathematical func- tions. Table 3.7 is a partial list of class methods available in the Math class. The class also has two class constants PI and E for and the natural number e, respectively. Using the Math class constant and methods, we can express the preceding formula as (1.0 /2.0) * Math.sin( x - Math.PI / Math.sqrt(y) ) 3.6 The Math Class 109 Table Table 3.7 Math class methods for commonly used mathematical functions Class Argument Result Method Type Type Description Example abs( a ) int int Returns the absolute int abs(10) → 10 value of a. abs(5) → 5 long long Returns the absolute long value of a. float float Returns the absolute float value of a. double double Returns the absolute double value of a. acos( a )† double double Returns the arccosine acos(1) of a. → 3.14159 asin( a )† double double Returns the arcsine asin(1) of a. → 1.57079 atan( a )† double double Returns the arctangent atan(1) of a. → 0.785398 ceil( a ) double double Returns the smallest ceil(5.6) → 6.0 whole number greater ceil(5.0) → 5.0 than or equal to a. ceil(5.6) → 5.0 cos( a )† double double Returns the trigonometric cos(2) → 0.0 cosine of a. exp( a ) double double Returns the natural exp(2) number e (2.718 ...) → 7.389056099 raised to the power of a. wu23399_ch03.qxd 12/13/06 17:38 Page 109
  • 137. 110 Chapter 3 Numerical Data Table Table 3.7 Math class methods for commonly used mathematical functions (Continued) Class Argument Result Method Type Type Description Example floor( a ) double double Returns the largest floor(5.6) → 5.0 whole number less than floor(5.0) → 5.0 or equal to a. floor(5.6) → 6.0 log( a ) double double Returns the natural log(2.7183) logarithm (base e) of a. → 1.0 max( a, b ) int int Returns the larger of a max(10,20) and b. → 20 long long Same as above. float float Same as above. min(a, b) int int Returns the smaller of a min(10,20) and b. → 10 long long Same as above. float float Same as above. pow(a, b) double double Returns the number a pow( 2.0,3.0) raised to the power of b. → 8.0 random( ) none double Generates a random Examples given number greater than or in Chapter 5 equal to 0.0 and less than 1.0. round( a ) float int Returns the int value of round(5.6) → 6 a rounded to the round(5.4) → 5 nearest whole number. round(5.6) → 6 double long Returns the float value of a rounded to the nearest whole number. sin( a )† double double Returns the sin(2 ) trigonometric sine of a. → 1.0 sqrt( a ) double double Returns the square root sqrt(9.0) → 3.0 of a. tan( a )† double double Returns the trigono- tan(4) metric tangent of a. → 1.0 toDegrees double double Converts the given toDegrees(4) angle in radians to → 45.0 degrees. toRadians double double Reverse of toDegrees. toRadians(90.0) → 1.5707963 † All trigonometric functions are computed in radians. wu23399_ch03.qxd 12/13/06 17:38 Page 110
  • 138. Notice how the class methods and class constants are referred to in the ex- pression. The syntax is class name . method name ( arguments ) or class name . class constant Let’s conclude this section with a sample program. Today is the final meet of the women’s rowing team against the arch rival university before the upcoming Division I NCAA championship. The cheerleaders of the rival team hoisted their school flag on the other shore of the river to boost their moral. Not to be outdone, we want to hoist our school flag, too. To bring the Goddess of Victory to our side, we want our pole to be taller than theirs. Since they won’t let us, we can’t find the height of their pole by actually measuring it. We can, however, determine the height without actually measuring it if we know the distance b to their flagpole. We can use the tangent of angle to determine the pole’s height h as follows: Unfortunately, there’s no means for us to go across the river to find out the dis- tance b. After a moment of deep meditation, it hits us that there’s no need to go across the river. We can determine the pole’s height by measuring angles from two points on this side of the riverbank, as shown below: h d A B h h b · tan b 3.6 The Math Class 111 wu23399_ch03.qxd 12/13/06 17:38 Page 111
  • 139. And the equation to compute the height h is h Once we have this equation, all that’s left is to put together a Java program. Here’s the program: d sin sin sin( ) sin ( ) 112 Chapter 3 Numerical Data /* Chapter 3 Sample Program: Estimate the Pole Height File: Ch3PoleHeight.java */ import java.text.*; import java.util.*; class Ch3PoleHeight { public static void main( String[] args ) { double height; //height of the pole double distance; //distance between points A and B double alpha; //angle measured at point A double beta; //angle measured at point B double alphaRad; //angle alpha in radians double betaRad; //angle beta in radians Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); //Get three input values System.out.print(Angle alpha (in degrees):); alpha = scanner.nextDouble(); System.out.print(Angle beta (in degree):); beta = scanner.nextDouble(); System.out.print(Distance between points A and B (ft):); distance = scanner.nextDouble(); //compute the height of the tower alphaRad = Math.toRadians(alpha); betaRad = Math.toRadians(beta); height = ( distance * Math.sin(alphaRad) * Math.sin(betaRad) ) / Math.sqrt( Math.sin(alphaRad + betaRad) * Math.sin(alphaRad - betaRad) ); wu23399_ch03.qxd 12/13/06 17:38 Page 112
  • 140. 3.7 Random Number Generation 113 DecimalFormat df = new DecimalFormat(0.000); System.out.println(lnln Estimating the height of the pole + nn + Angle at point A (deg): + df.format(alpha) + n + Angle at point B (deg): + df.format(beta) + n + Distance between A and B (ft): + df.format(distance)+ n + Estimated height (ft): + df.format(height)); } } 1. What’s wrong with the following? a. y = (1/2) * Math.sqrt( X ); b. y = sqrt(38.0); c. y = Math.exp(2, 3); d. y = math.sqrt( b*b - 4*a*c) / ( 2 * a ); 2. If another programmer writes the following statements, do you suspect any misunderstanding on the part of this programmer? What will be the value of y? a. y = Math.sin( 360 ) ; b. y = Math.cos( 45 ); 3.7 Random Number Generation In many computer applications, especially in simulation and games, we need to gen- erate random numbers. For example, to simulate a roll of dice, we can generate an integer between 1 and 6. In this section, we explain how to generate random numbers using the random method of the Math class. (Alternatively, you can use the Random class. We refer you to the JavaAPI documentation for information on this class.) The method random is called a pseudorandom number generator and returns a number (type double) that is greater than or equal to 0.0 but less than 1.0, that is, 0.0 X 1.0. The generated number is called a pseudorandom number because the number is not truly random. When we call this method repeatedly, eventually the numbers generated will repeat themselves. Therefore, theoretically the generated numbers are not random; but for all practical purposes, they are random enough. The random numbers we want to generate for most applications are integers. For example, to simulate the draw of a card, we need to generate an integer between 1 and 4 for the suit and an integer between 1 and 13 for the number. Since the number returned from the random method ranges from 0.0 up to but not including 1.0, we need to perform some form of conversion so the converted number will fall in our de- sired range. Let’s assume the range of integer values we want is [min, max]. If X is a pseudorandom number generator wu23399_ch03.qxd 12/13/06 17:38 Page 113
  • 141. number returned by random, then we can convert it into a number Y such that Y is in the range [min, max] that is, min Y max by applying the following fourmula: Y X (max min 1) min For many applications, the value for min is 1, so the formula is simplified to Y X max 1 Expressing the general formula in Java will result in the following statement: //assume correct values are assigned to 'max' and 'min' int randomNumber = (int) (Math.floor(Math.random() * ( max-min+1)) + min); Notice that we have to typecast the result of Math.floor to int because the data type of the result is double. Let’s write a short program that selects a winner among the party goers of the annual spring fraternity dance. The party goers will receive numbers M 1, M 2, M 3, and so on, as they enter the house. The starting value M is selected by the chairperson of the party committee. The last number assigned is M N if there are N party goers. At the end of the party, we run the program that will randomly select the winning number from the range of M 1 and M N. Here’s the program: 114 Chapter 3 Numerical Data /* Chapter 3 Sample Program: Select the Winning Number File: Ch3SelectWinner.java */ import java.util.*; class Ch3SelectWinner { public static void main(String[] args) { int startingNumber; //the starting number int count; //the number of party goers int winningNumber; //the winner int min, max; //the range of random numbers to generate Scanner scan = new Scanner(System.in); //Get two input values System.out.print(Enter the starting number M: ); startingNumber = scan.nextInt(); System.out.print(Enter the number of party goers: ); count = scan.nextInt(); wu23399_ch03.qxd 1/11/07 11:49 Page 114
  • 142. 3.8 The GregorianCalendar Class In Chapter 2, we introduced the java.util.Date class to represent a specific instant in time. Notice that we are using here the more concise expression “the java.util.Date class” to refer to a class from a specific package instead of the longer expression “the Date class from the java.util package.” This shorter version is our preferred way of notation when we need or want to identify the package to which the class belongs. 3.8 The GregorianCalendar Class 115 //select the winner min = startingNumber + 1; max = startingNumber + count; winningNumber = (int) ( Math.floor(Math.random() * (max - min + 1)) + min); System.out.println(nThe Winning Number is + winningNumber); } } When we need to identify the specific package to which a class belongs,we will commonly use the concise expression with the full path name,such as java.util.Date, instead of writing“the Date class from the java.util package.” In addition to this class, we have a very useful class named java.util.Gregorian- Calendar in manipulating calendar information such as year, month, and day. We can create a new GregorianCalendar object that represents today as GregorianCalendar today = new GregorianCalendar( ); or a specific day, say, July 4, 1776, by passing year, month, and day as the parame- ters as GregorianCalendar independenceDay = new GregorianCalendar(1776, 6, 4); No, the value of 6 as the second parameter is not an error. The first month of a year, January, is represented by 0, the second month by 1, and so forth. To avoid confusion, we can use constants defined for months in the superclass Calendar (GregorianCalendar is a subclass of Calendar). Instead of remembering that the The value of 6 means July. Gregorian- Calendar wu23399_ch03.qxd 12/13/06 17:38 Page 115
  • 143. value 6 represents July, we can use the defined constant Calendar.JULY as GregorianCalendar independenceDay = new GregorianCalendar(1776, Calendar.JULY, 4); Table 3.8 explains the use of some of the more common constants defined in the Calendar class. When the date and time are November 11, 2002, 6:13 p.m. and we run the Ch3TestCalendar program, we will see the result shown in Figure 3.4. 116 Chapter 3 Numerical Data Table Table 3.8 Constant Description YEAR The year portion of the calendar date MONTH The month portion of the calendar date DATE The day of the month DAY_OF_MONTH Same as DATE DAY_OF_YEAR The day number within the year DAY_OF_MONTH The day number within the month DAY_OF_WEEK The day of the week (Sun—1,Mon—2,etc.) WEEK_OF_YEAR The week number within the year WEEK_OF_MONTH The week number within the month AM_PM The indicator for AM or PM (AM—0 and PM—1) HOUR The hour in 12-hour notation HOUR_OF_DAY The hour in 24-hour notation MINUTE The minute within the hour Constants defined in the Calendar class for retrieved different pieces of calendar/time information Figure 3.4 Result of running the Ch3TestCalender program at November 11,2002,6:13 p.m. wu23399_ch03.qxd 12/13/06 17:38 Page 116
  • 144. Notice that the first line in the output shows the full date and time information. The full date and time information can be accessed by calling the calendar object’s getTime method. This method returns the same information as a Date object. Notice also that we get only the numerical values when we retrieve the day of the week or month information. We can spell out the information by using the SimpleDateFormat class. Since the constructor of the SimpleDateFormat class ac- cepts only the Date object, first we need to convert a GregorianCalendar object to an equivalent Date object by calling its getTime method. For example, here’s how 3.8 The GregorianCalendar Class 117 /* Chapter 3 Sample Program: Display Calendar Info File: Ch3TestCalendar.java */ import java.util.*; class Ch3TestCalendar { public static void main(String[] args) { GregorianCalendar cal = new GregorianCalendar(); System.out.println(cal.getTime()); System.out.println(); System.out.println(YEAR: + cal.get(Calendar.YEAR)); System.out.println(MONTH: + cal.get(Calendar.MONTH)); System.out.println(DATE: + cal.get(Calendar.DATE)); System.out.println(DAY_OF_YEAR: + cal.get(Calendar.DAY_OF_YEAR)); System.out.println(DAY_OF_MONTH: + cal.get(Calendar.DAY_OF_MONTH)); System.out.println(DAY_OF_WEEK: + cal.get(Calendar.DAY_OF_WEEK)); System.out.println(WEEK_OF_YEAR: + cal.get(Calendar.WEEK_OF_YEAR)); System.out.println(WEEK_OF_MONTH: + cal.get(Calendar.WEEK_OF_MONTH)); System.out.println(AM_PM: + cal.get(Calendar.AM_PM)); System.out.println(HOUR: + cal.get(Calendar.HOUR)); System.out.println(HOUR_OF_DAY: + cal.get(Calendar.HOUR_OF_DAY)); System.out.println(MINUTE: + cal.get(Calendar.MINUTE)); } } getTime wu23399_ch03.qxd 12/13/06 17:38 Page 117
  • 145. we can display the day of the week on which our Declaration of Independence was adopted in Philadelphia: 118 Chapter 3 Numerical Data /* Chapter 3 Sample Program: Day of the week the Declaration of Independence was adopted File: Ch3IndependenceDay.java */ import java.util.*; import java.text.*; class Ch3IndependenceDay { public static void main(String[] args) { GregorianCalendar independenceDay = new GregorianCalendar(1776, Calendar.JULY, 4); SimpleDateFormat sdf = new SimpleDateFormat(EEEE); System.out.println(It was adopted on + sdf.format(independenceDay.getTime())); } } Let’s finish the section with a sample program that extends the Ch3Indepen- denceDay program. We will allow the user to enter the year, month, and day; and we will reply with the day of the week of the given date (our birthday, grandparent’s wedding day, and so on). Here’s the program: /* Chapter 3 Sample Program: Find the Day of Week of a Given Date File: Ch3FindDayOfWeek.java */ import java.util.*; import java.text.*; class Ch3FindDayOfWeek { public static void main(String[] args) { int year, month, day; GregorianCalendar cal; SimpleDateFormat sdf; wu23399_ch03.qxd 12/13/06 17:38 Page 118
  • 146. Notice that we are allowing the user to enter the month as an integer be- tween 1 and 12, so we need to subtract 1 from the entered data in creating a new GregorianCalendar object. 3.8 The GregorianCalendar Class 119 Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); System.out.print(Year (yyyy): ); year = scanner.nextInt(); System.out.print(Month (1-12): ); month = scanner.nextInt(); System.out.print(Day (1-31): ); day = scanner.nextInt(); cal = new GregorianCalendar(year, month-1, day); sdf = new SimpleDateFormat(EEEE); System.out.println(); System.out.println(Day of Week: + sdf.format(cal.getTime())); } } The Gregorian calendar system was adopted by England and its colonies, including the colonial United States, in 1752. So the technique shown here works only after this adoption. For a fascinating story about calendars, visit http://webexhibits.org/calendars/year-countries.html Running Ch3IndpendenceDay will tell you that our venerable document was signed on Thursday. History textbooks will say something like “the document was formally adopted July 4, 1776, on a bright, but cool Philadelphia day” but never the day of the week. Well, now you know. See how useful Java is? By the way, the document was adopted by the Second Continental Congress on July 4, but the actual signing did not take place until August 2 (it was Friday—what a great reason for a TGIF party) after the approval of all 13 colonies. For more stories behind the Declaration of Independence, visit http://www.ushistory.org/declaration/ wu23399_ch03.qxd 12/13/06 17:38 Page 119
  • 147. Loan Calculator In this section,we develop a simple loan calculator program.We develop this program by using an incremental development technique, which develops the program in small in- cremental steps.We start out with a bare-bones program and gradually build up the pro- gram by adding more and more code to it.At each incremental step,we design,code,and test the program before moving on to the next step. This methodical development of a program allows us to focus our attention on a single task at each step, and this reduces the chance of introducing errors into the program. Problem Statement The next time you buy a new TV or a stereo, watch out for those “0% down, 0% interest until next July”deals. Read the fine print, and you’ll notice that if you don’t make the full payment by the end of a certain date,hefty interest will start accruing.You may be better off to get an ordinary loan from the beginning with a cheaper interest rate.What matters most is the total payment (loan amount plus total interest) you’ll have to make.To com- pare different loan deals, let’s develop a loan calculator. Here’s the problem statement: Write a loan calculator program that computes both monthly and total payments for a given loan amount,annual interest rate,and loan period. Overall Plan Our first task is to map out the overall plan for development.We will identify classes nec- essary for the program and the steps we will follow to implement the program.We begin with the outline of program logic.For a simple program such as this one,it is kind of obvi- ous;but to practice the incremental development,let’s put down the outline of program flow explicitly.We can express the program flow as having three tasks: 1. Get three input values:loanAmount, interestRate, and loanPeriod. 2. Compute the monthly and total payments. 3. Output the results. Having identified the three major tasks of the program,we now identify the classes we can use to implement the three tasks. For input and output, we continue to use the Scanner class and System.out (PrintStream). For computing the monthly and total payments, there are no standard classes that will provide such computation, so we have to write our own code. The formula for computing the monthly payment can be found in any mathemat- ics book that covers geometric sequences.It is Monthly payment L R 1 [1(1 R)]N Sample Development 3.9 Sample Development 120 Chapter 3 Numerical Data program tasks wu23399_ch03.qxd 12/13/06 17:38 Page 120
  • 148. where L is the loan amount,R is the monthly interest rate,and N is the number of payments. ThemonthlyrateRisexpressedinafractionalvalue,for example,0.01for 1 percent monthly rate.Once the monthly payment is derived,the total payment can be determined by multi- plying the monthly payment by the number of months the payment is made.Since the for- mula includes exponentiation,we will have to use the pow method of the Math class. Let’s summarize what we have decided so far in a design document: 3.9 Sample Development 121 program classes Design Document: LoanCalculator Class Purpose LoanCalculator The main class of the program. Scanner The class is used to get three input values:loan amount, annual interest rate,and loan period. PrintStream System.out is used to display the input values and two (System.out) computed results:monthly payment and total payment. Math The pow method is used to evaluate exponentiation in the formula for computing the monthly payment.This class is from java.lang. Note: You don’t have to import java.lang.The classes in java.lang are available to a program without importing. The program diagram based on the classes listed in the design document is shown in Figure 3.5. Keep in mind that this is only a preliminary design. The preliminary docu- ment is really a working document that we will modify and expand as we progress through the development steps. Before we can actually start our development, we must sketch the steps we will follow to implement the program.There is more than one possible sequence of steps to implement a program,and the number of possible sequences will increase as the program becomes more complex. For this program, we will implement the program in four steps: 1. Start with code to accept three input values. 2. Add code to output the results. 3. Add code to compute the monthly and total payments. 4. Update or modify code and tie up any loose ends. Notice how the first three steps are ordered. Other orders are possible to develop this program.So why did we choose this particular order?The main reason is our desire to defer the most difficult task until the end.It’s possible,but if we implement the computa- tion part in the second incremental step, then we need to code some temporary output routines to verify that the computation is done correctly. However, if we implement the real output routines before implementing the computation routines, then there is no develop- ment steps wu23399_ch03.qxd 12/13/06 17:38 Page 121
  • 149. 122 Chapter 3 Numerical Data need for us to worry about temporary output routines.As for step 1 and step 2,their rela- tive order does not matter much.We simply chose to implement the input routine before the output routine because input comes before output in the program. Step 1 Development: Input Three Data Values The next task is to determine how we will accept the input values.The problem statement does not specify the exact format of input,so we will decide that now.Based on how peo- ple normally refer to loans,the input values will be accepted in the following format: Input Format Data Type Loan amount In dollars and cents (for example,15000.00) double Annual interest rate In percent (for example,12.5) double Loan period In years (for example,30) int Be aware that we need to convert the annual interest rate to the monthly interest rate and the input value loan period to the number of monthly payments, to use the given formula. In this case, the conversion is very simple, but even if the conversion routines were more complicated, we must do the conversion. It is not acceptable to ask users to step 1 design 3.9 Sample Development—continued Figure 3.5 The object diagram for the program LoanCalculator. LoanCalculator Scanner Math System.out : PrintStream wu23399_ch03.qxd 12/13/06 17:38 Page 122
  • 150. 3.9 Sample Development 123 enter an input value that is unnatural to them.For example, people do not think of inter- est rates in fractional values such as 0.07. They think of interest in terms of percentages such as 7 percent. Computer programs work for humans, not the other way round. Programs we develop should not support an interface that is difficult and awkward for humans to use. When the user inputs an invalid value, for example, an input string value that can- not be converted to a numerical value or that converts to a negative number,the program should respond accordingly, such as by printing an error message. We do not possess enough skills to implement such a robust program yet, so we will make the following assumptions: (1) The input values are nonnegative numbers, and (2) the loan period is a whole number. One important objective of this step is to verify that the input values are read in correctly by the program. To verify this, we will echo-print the input values to System.out. Here’s our step 1 program: step 1 code /* Chapter 3 Sample Development: Loan Calculator (Step 1) File: Step1/Ch3LoanCalculator.java Step 1: Input Data Values */ import java.util.*; class Ch3LoanCalculator { public static void main(String[] args) { double loanAmount, annualInterestRate; int loanPeriod; Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); //get input values System.out.print(Loan Amount (Dollars+Cents): ); loanAmount = scanner.nextDouble(); System.out.print(Annual Interest Rate (e.g., 9.5): ); annualInterestRate = scanner.nextDouble(); System.out.print(Loan Period - # of years: ); loanPeriod = scanner.nextInt(); wu23399_ch03.qxd 12/13/06 17:38 Page 123
  • 151. 3.9 Sample Development—continued 124 Chapter 3 Numerical Data //echo print the input values System.out.println (); System.out.println(Loan Amount: $ + loanAmount); System.out.println(Annual Interest Rate: + annualInterestRate + %); System.out.println(Loan Period (years): + loanPeriod); } } To verify the input routine is working correctly, we run the program multiple times and enter different sets of data. We make sure the values are displayed in the standard output window as entered. Step 2 Development: Output Values The second step is to add code to display the output values.We will use the standard out- put window for displaying output values.We need to display the result in a layout that is meaningful and easy to read. Just displaying numbers such as the following is totally unacceptable. 132.151.15858.1 We must label the output values so the user can tell what the numbers represent.In addi- tion, we must display the input values with the computed result so it will not be mean- ingless. Which of the two shown in Figure 3.6 do you think is more meaningful? The output format of this program will be For Loan Amount: $ amount Annual Interest Rate: annual interest rate % Loan Period (years): year Monthly payment is $ monthly payment TOTAL payment is $ total payment with amount,annual interest rate, and others replaced by the actual figures. step 1 test step 2 design wu23399_ch03.qxd 12/13/06 17:38 Page 124
  • 152. 3.9 Sample Development 125 Since the computations for the monthly and total payments are not yet imple- mented,we will use the following dummy assignment statements: monthlyPayment = 135.15; totalPayment = 15858.10; We will replace these statements with the real ones in the next step. Here’s our step 2 program with the newly added portion surrounded by a rectangle and white background: Only the computed values (and their labels) are shown. Monthly payment: $ 143.47 Total payment: $ 17216.50 Both the input and computed values (and their labels) are shown. For Loan Amount: $ 10000.00 Annual Interest Rate: 12.0% Loan Period (years): 10 Monthly payment is $ 143.47 TOTAL payment is $ 17216.50 Figure 3.6 Two different display formats,one with input values displayed and the other with only the computed values displayed. step 2 code /* Chapter 3 Sample Development: Loan Calculator (Step 2) File: Step2/Ch3LoanCalculator.java Step 2: Display the Results */ import java.util.*; class Ch3LoanCalculator { public static void main(String[] args) { double loanAmount, annualInterestRate; double monthlyPayment, totalPayment; int loanPeriod; wu23399_ch03.qxd 12/13/06 17:38 Page 125
  • 153. 126 Chapter 3 Numerical Data Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); //get input values System.out.print(Loan Amount (Dollars+Cents): ); loanAmount = scanner.nextDouble(); System.out.print(Annual Interest Rate (e.g., 9.5): ); annualInterestRate = scanner.nextDouble(); System.out.print(Loan Period - # of years: ); loanPeriod = scanner.nextInt(); //compute the monthly and total payments monthlyPayment = 132.15; totalPayment = 15858.10; //display the result System.out.println(); System.out.println(Loan Amount: $ + loanAmount); System.out.println(Annual Interest Rate: + annualInterestRate + %); System.out.println(Loan Period (years): + loanPeriod); System.out.println(n); //skip two lines System.out.println(Monthly payment is $ + monthlyPayment); System.out.println( TOTAL payment is $ + totalPayment); } } To verify the output routine is working correctly,we run the program and verify the layout.Most likely,we have to run the program several times to fine-tune the arguments for the println methods until we get the layout that looks clean and nice on the screen. Step 3 Development: Compute Loan Amount Wearenowreadytocompletetheprogrambyimplementingtheformuladerivedinthede- sign phase.The formula requires the monthly interest rate and the number of monthly pay- ments.The input values to the program,however,are the annual interest rate and the loan periodinyears.So weneedtoconverttheannualinterestratetoamonthlyinterestrateand the loan period to the number of monthly payments.The two input values are converted as monthlyInterestRate = annualInterestRate / 100.0 / MONTHS_IN_YEAR; numberOfPayments = loanPeriod * MONTHS_IN_YEAR; step 2 test step 3 design 3.9 Sample Development—continued wu23399_ch03.qxd 1/12/07 10:36 Page 126
  • 154. 3.9 Sample Development 127 where MONTHS_IN_YEAR is a symbolic constant with value 12. Notice that we need to divide the input annual interest rate by 100 first because the formula for loan computa- tion requires that the interest rate be a fractional value, for example, 0.01, but the input annual interest rate is entered as a percentage point, for example, 12.0. Please read Exer- cise 23 on page 142 for information on how the monthly interest rate is derived from a given annual interest rate. The formula for computing the monthly and total payments can be expressed as monthlyPayment = (loanAmount * monthlyInterestRate) / (1 - Math.pow( 1 /(1 + monthlyInterestRate), numberOfPayments) ); totalPayment = monthlyPayment * numberOfPayments; Let’s put in the necessary code for the computations and complete the program. Here’s our program: step 3 code /* Chapter 3 Sample Development: Loan Calculator (Step 3) File: Step3/Ch3LoanCalculator.java Step 3: Display the Results */ import java.util.*; class Ch3LoanCalculator { public static void main(String[] args) { final int MONTHS_IN_YEAR = 12; double loanAmount, annualInterestRate; double monthlyPayment, totalPayment; double monthlyInterestRate; int loanPeriod; int numberOfPayments; wu23399_ch03.qxd 12/13/06 17:38 Page 127
  • 155. 3.9 Sample Development—continued 128 Chapter 3 Numerical Data Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); //get input values System.out.print(Loan Amount (Dollars+Cents): ); loanAmount = scanner.nextDouble(); System.out.print(Annual Interest Rate (e.g., 9.5): ); annualInterestRate = scanner.nextDouble(); System.out.print(Loan Period - # of years: ); loanPeriod = scanner.nextInt(); //compute the monthly and total payments monthlyInterestRate = annualInterestRate / MONTHS_IN_YEAR / 100; numberOfPayments = loanPeriod * MONTHS_IN_YEAR; monthlyPayment = (loanAmount * monthlyInterestRate)/ (1 - Math.pow(1/(1 + monthlyInterestRate), numberOfPayments ) ); totalPayment = monthlyPayment * numberOfPayments; //display the result System.out.println(); System.out.println(Loan Amount: $ + loanAmount); System.out.println(Annual Interest Rate: + annualInterestRate + %); System.out.println(Loan Period (years): + loanPeriod); System.out.println(n); //skip two lines System.out.println(Monthly payment is $ + monthlyPayment); System.out.println( TOTAL payment is $ + totalPayment); } } After the program is coded,we need to run the program through a number of tests. Since we made the assumption that the input values must be valid, we will test the pro- gram only for valid input values. If we don’t make that assumption, then we need to test that the program will respond correctly when invalid values are entered.We will perform such testing beginning in Chapter 5.To check that this program produces correct results, step 3 test wu23399_ch03.qxd 12/13/06 17:38 Page 128
  • 156. 3.9 Sample Development 129 Output (shown up to three decimal Input places only) Annual Loan Loan Interest Period Monthly Total Amount Rate (Years) Payment Payment 10000 10 10 132.151 15858.088 15000 7 15 134.824 24268.363 10000 12 10 143.471 17216.514 0 10 5 0.000 0.000 30 8.5 50 0.216 129.373 Step 4 Development: Finishing Up We finalize the program in the last step by making any necessary modifications or addi- tions.We will make two additions to the program.The first is necessary while the second is optional but desirable.The first addition is the inclusion of a program description. One of the necessary features of any nontrivial program is the description of what the pro- gram does for the user.We will print out a description at the beginning of the program to System.out. The second addition is the formatting of the output values. We will format the monthly and total payments to two decimal places, using a DecimalFormat object. Here is our final program: step 4 design step 4 code /* Chapter 3 Sample Development: Loan Calculator (Step 4) File: Step4/Ch3LoanCalculator.java Step 4: Finalize the program */ import java.util.*; import java.text.*; class Ch3LoanCalculator { public static void main(String[] args) { final int MONTHS_IN_YEAR = 12; we can run the program with the following input values.The right two columns show the correct results.Try other input values as well. wu23399_ch03.qxd 12/13/06 17:38 Page 129
  • 157. 3.9 Sample Development—continued 130 Chapter 3 Numerical Data double loanAmount, annualInterestRate; double monthlyPayment, totalPayment; double monthlyInterestRate; int loanPeriod; int numberOfPayments; Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); DecimalFormat df = new DecimalFormat(0.00); //describe the program System.out.println(This program computes the monthly and total); System.out.println(payments for a given loan amount, annual ); System.out.println(interest rate, and loan period.); System.out.println(Loan amount in dollars and cents, e.g., 12345.50); System.out.println(Annual interest rate in percentage, e.g., 12.75); System.out.println(Loan period in number of years, e.g., 15); System.out.println(n); //skip two lines //get input values System.out.print(Loan Amount (Dollars+Cents): ); loanAmount = scanner.nextDouble( ); System.out.print(Annual Interest Rate (e.g., 9.5): ); annualInterestRate = scanner.nextDouble( ); System.out.print(Loan Period - # of years: ); loanPeriod = scanner.nextInt( ); //compute the monthly and total payments monthlyInterestRate = annualInterestRate / MONTHS_IN_YEAR / 100; numberOfPayments = loanPeriod * MONTHS_IN_YEAR; monthlyPayment = (loanAmount * monthlyInterestRate) / (1 - Math.pow(1/(1 + monthlyInterestRate), numberOfPayments ) ); totalPayment = monthlyPayment * numberOfPayments; wu23399_ch03.qxd 12/13/06 17:38 Page 130
  • 158. 3.10 Numerical Representation (Optional) In this section we explain how integers and real numbers are stored in memory. Although computer manufacturers have used various formats for storing numerical values, today’s standard is to use the twos complement format for storing integers and the floating-point format for real numbers. We describe these formats in this section. An integer can occupy 1, 2, 4, or 8 bytes depending on which data type (i.e., byte, short, int, or long) is declared. To make the examples easy to follow, we will use 1 byte ( 8 bits) to explain twos complement form. The same principle applies to 2, 4, and 8 bytes. (They just utilize more bits.) 3.10 Numerical Representation (Optional) 131 //display the result System.out.println(); System.out.println(Loan Amount: $ + loanAmount); System.out.println(Annual Interest Rate: + annualInterestRate + %); System.out.println(Loan Period (years): + loanPeriod); System.out.println(n); //skip two lines System.out.println(Monthly payment is $ + df.format(monthlyPayment)); System.out.println( TOTAL payment is $ + df.format(totalPayment)); } } We repeat the test runs from step 3 and confirm the modified program still runs correctly. Since we have not made any substantial additions or modifications, we fully expect the program to work correctly. However, it is very easy to introduce errors in cod- ing,so even if we think the changes are trivial,we should never skip the testing after even a slight modification. step 4 test Always test after making any additions or modifications to a program,no matter how trivial you think the changes are. twos complement wu23399_ch03.qxd 12/13/06 17:38 Page 131
  • 159. 132 Chapter 3 Numerical Data The following table shows the first five and the last four of the 256 positive binary numbers using 8 bits. The right column lists their decimal equivalents. 8-Bit Binary Number Decimal Equivalent 00000000 0 00000001 1 00000010 2 00000011 3 00000100 4 . . . 11111100 252 11111101 253 11111110 254 11111111 255 Using 8 bits, we can represent positive integers from 0 to 255. Now let’s see the possible range of negative and positive numbers that we can represent, using 8 bits. We can designate the leftmost bit as a sign bit: 0 means positive and 1 means negative. Using this scheme, we can represent integers from 127 to 127 as shown in the following table: sign bit 8-Bit Binary Number (with a Sign Bit) Decimal Equivalent 0 0000000 0 0 0000001 1 0 0000010 2 . . . 0 1111111 127 1 0000000 0 1 0000001 1 . . . 1 1111110 126 1 1111111 127 Notice that zero has two distinct representations (0 00000000 and 0 10000000), which adds complexity in hardware design. Twos complement format avoids this problem of duplicate representations for zero. In twos complement for- mat, all positive numbers have zero in their leftmost bit. The representation of a negative number is derived by first inverting all the bits (changing 1s to 0s and 0s to wu23399_ch03.qxd 12/13/06 17:38 Page 132
  • 160. 3.10 Numerical Representation (Optional) 133 1s) in the representation of the positive number and then adding 1. The following diagram illustrates the process: 13 = 00001101 invert 11110010 add 1 -13 = 11110011 The following table shows the decimal equivalents of 8-bit binary numbers by using twos complement representation. Notice that zero has only one representation. 8-Bit Binary Number Decimal (Twos Complement) Equivalent 00000000 0 00000001 1 00000010 2 . . . 01111111 127 10000000 128 10000001 127 . . . 11111110 2 11111111 1 Now let’s see how real numbers are stored in memory in floating-point format. We present only the basic ideas of storing real numbers in computer memory here. We omit the precise details of the Institute of Electronics and Electrical Engineers (IEEE) Standard 754 that Java uses to store real numbers. Real numbers are represented in the computer by using scientific notation. In base-10 scientific notation, a real number is expressed as A 10N where A is a real number and N is an integral exponent. For example, the mass of a hydrogen atom (in grams) is expressed in decimal scientific notation as 1.67339 10–24 , which is equal to 0.00000000000000000000000167339. We use base-2 scientific notation to store real numbers in computer memory. Base-2 scientific notation represents a real number as follows: A 2N The float and double data types use 32 and 64 bits, respectively, with the num- ber A and exponent N stored as follows: floating-point wu23399_ch03.qxd 12/13/06 17:38 Page 133
  • 161. 134 Chapter 3 Numerical Data 8 1 A N S 1 A N S 11 52 23 Sign bit: 0 — positive 1 — negative Number of bits used The value A is a normalized fraction, where the fraction begins with a binary point, followed by a 1 bit and the rest of the fraction. ( Note: A decimal number has a dec- imal point; a binary number has a binary point.) The following numbers are sample normalized and unnormalized binary fractions: normalized fraction Normalized Unnormalized 1.1010100 1.100111 1.100011 .0000000001 1.101110011 .0001010110 Since a normalized number always start with a 1, this bit does not actually have to be stored. The following diagram illustrates how the A value is stored. The sign bit S indicates the sign of a number, so A is stored in memory as an un- signed number. The integral exponent N can be negative or positive. Instead of using twos complement for storing N, we use a format called excess format. The 8-bit ex- ponent uses the excess-127 format, and the 11-bit exponent uses the excess-1023 format. We will explain the excess-127 format here. The excess-1023 works similarly. With the excess-127 format, the actual exponent is computed as N 127 Therefore, the number 127 represents an exponent of zero. Numbers less than 127 represent negative exponents, and numbers greater than 127 represent positive exponents. The following diagram illustrates that the number 125 in the exponent field represents 2125127 22 . N 01111101 A S 201111101127 2125127 22 8 1 0 1 1 0 1 1 1 0 0 ... 0 0 0 1 1 0 1 1 1 .1 N S excess format wu23399_ch03.qxd 12/13/06 17:38 Page 134
  • 162. Key Concepts 135 • A variable is a memory location in which to store a value. • A variable has a name and a data type. • A variable must be declared before we can assign a value to it. • There are six numerical data types in Java: byte, short, int, long, float, and double. • Object names are synonymous with variables whose contents are memory addresses. • Numerical data types are called primitive data types, and objects are called reference data types. • Precedence rules determine the order of evaluating arithemetic expressions. • Symbolic constants hold values just as variables do, but we cannot change their values. • The standard classes introduced in this chapter are Math GregorianCalendar DecimalFormat PrintStream • System.out is used to output multiple lines of text to the standard output window. • System.in is used to input a stream of bytes. We associate a Scanner object to System.in to input primitive data type. • The Math class contains many class methods for mathematical functions. • The GregorianCalendar class is used in the manipulation of calendar information. • The DecimalFormat class is used to format numerical data. • (Optional) Twos complement format is used for storing integers, and floating-pointing format is used for storing real numbers. S u m m a r y K e y C o n c e p t s variables primitive data types reference data types arithmetic expression arithmetic operators precedence rules typecasting implicit and explicit casting assignment conversion constants standard output standard input echo printing twos complement (optional) floating point (optional) wu23399_ch03.qxd 12/13/06 17:38 Page 135
  • 163. 136 Chapter 3 Numerical Data E x e r c i s e s 1. Suppose we have the following declarations: int i = 3, j = 4, k = 5; float x = 34.5f, y = 12.25f; Determine the value for each of the following expressions, or explain why it is not a valid expression. a. (x + 1.5) / (250.0 * (i/j)) b. x + 1.5 / 250.0 * i / j c. -x * -y * (i + j) / k d. (i / 5) * y e. Math.min(i, Math.min(j,k)) f. Math.exp(3, 2) g. y % x h. Math.pow(3, 2) i. (int)y % k j. i / 5 * y 2. Suppose we have the following declarations: int m, n, i = 3, j = 4, k = 5; float v, w, x = 34.5f, y = 12.25f; Determine the value assigned to the variable in each of the following assignment statements, or explain why it is not a valid assignment. a. w = Math.pow(3,Math.pow(i,j)); b. v = x / i; c. w = Math.ceil(y) % k; d. n = (int) x / y * i / 2; e. x = Math.sqrt(i*i - 4*j*k); f. m = n + i * j; g. n = k /(j * i) * x + y; h. i = i + 1; i. w = float(x + i); j. x = x / i / y / j; 3. Suppose we have the following declarations: int i, j; float x, y; double u, v; Which of the following assignments are valid? a. i = x; b. x = u + y; c. x = 23.4 + j * y; d. v = (int) x; e. y = j / i * x; wu23399_ch03.qxd 12/13/06 17:38 Page 136
  • 164. 4. Write Java expressions to compute each of the following. a. The square root of B2 4AC (A and C are distinct variables) b. The square root of X 4Y3 c. The cube root of the product of X and Y d. The area R2 of a circle 5. Determine the output of the following program without running it. class TestOutputBox { public static void main(String[] args) { System.out.println(One); System.out.print(Two); System.out.print(n); System.out.print(Three); System.out.println(Four); System.out.print(n); System.out.print(Five); System.out.println(Six); } } 6. Determine the output of the following code. int x, y; x = 1; y = 2; System.out.println(The output is + x + y ); System.out.println(The output is + ( x + y) ); 7. Write an application that displays the following pattern in the standard output window. Note: The output window is not drawn to scale. 8. Write an application to convert centimeters (input) to feet and inches (output). 1 in 2.54 cm. OXOXOXOXOXOXOXOXOXOX X O O X X O O X X O OXOXOXOXOXOXOXOXOXOX Starts from the second line with five leading blank spaces. Exercises 137 wu23399_ch03.qxd 12/13/06 17:38 Page 137
  • 165. 9. Write an application that inputs temperature in degrees Celsius and prints out the temperature in degrees Fahrenheit. The formula to convert degrees Celsius to equivalent degrees Fahrenheit is Fahrenheit 1.8 Celsius 32 10. Write an application that accepts a person’s weight and displays the number of calories the person needs in one day. A person needs 19 calories per pound of body weight, so the formula expressed in Java is calories = bodyWeight * 19; (Note: We are not distinguishing between genders.) 11. A quantity known as the body mass index (BMI) is used to calculate the risk of weight-related health problems. BMI is computed by the formula BMI (h10 w 0.0)2 where w is weight in kilograms and h is height in centimeters. A BMI of about 20 to 25 is considered “normal.” Write an application that accepts weight and height (both integers) and outputs the BMI. 12. Your weight is actually the amount of gravitational attraction exerted on you by the Earth. Since the Moon’s gravity is only one-sixth of the Earth’s gravity, on the Moon you would weigh only one-sixth of what you weigh on Earth. Write an application that inputs the user’s Earth weight and outputs her or his weight on Mercury, Venus, Jupiter, and Saturn. Use the values in this table. Planet Multiply the Earth Weight by Mercury 0.4 Venus 0.9 Jupiter 2.5 Saturn 1.1 13. When you say you are 18 years old, you are really saying that the Earth has circled the Sun 18 times. Since other planets take fewer or more days than Earth to travel around the Sun, your age would be different on other planets. You can compute how old you are on other planets by the formula y x d 365 where x is the age on Earth, y is the age on planet Y, and d is the number of Earth days the planet Y takes to travel around the Sun. Write an application that inputs the user’s Earth age and print outs his or her age on Mercury, Venus, Jupiter, and Saturn. The values for d are listed in the table. 138 Chapter 3 Numerical Data wu23399_ch03.qxd 12/13/06 17:38 Page 138
  • 166. d Approximate Number of Earth Days for This Planet to Travel Planet around the Sun Mercury 88 Venus 225 Jupiter 4,380 Saturn 10,767 14. Write an application to solve quadratic equations of the form Ax2 Bx C 0 where the coefficients A, B, and C are real numbers. The two real number solutions are derived by the formula x B 2 B A 2 4 AC For this exercise, you may assume that A 0 and the relationship B2 4AC holds, so there will be real number solutions for x. 15. Write an application that determines the number of days in a given semester. Input to the program is the year, month, and day information of the first and the last days of a semester. Hint: Create GregorianCalendar objects for the start and end dates of a semester and manipulate their DAY_OF_YEAR data. 16. Modify the Ch3FindDayOfWeek program by accepting the date information as a single string instead of accepting the year, month, and day information separately. The input string must be in the MM/dd/yyyy format. For example, July 4, 1776, is entered as 07/04/1776. There will be exactly two digits for the month and day and four digits for the year. 17. Write an application that accepts the unit weight of a bag of coffee in pounds and the number of bags sold and displays the total price of the sale, computed as totalPrice = unitWeight * numberOfUnits * 5.99; totalPriceWithTax = totalPrice + totalPrice * 0.0725; where 5.99 is the cost per pound and 0.0725 is the sales tax. Display the result in the following manner: Draw the program diagram. Number of bags sold: 32 Weight per bag: 5 lb Price per pound: $5.99 Sales tax: 7.25% Total price: $ 1027.884 Exercises 139 wu23399_ch03.qxd 12/13/06 17:38 Page 139
  • 167. 18. If you invest P dollars at R percent interest rate compounded annually, in N years, your investment will grow to P(1 R100)N dollars. Write an application that accepts P, R, and N and computes the amount of money earned after N years. 19. Leonardo Fibonacci of Pisa was one of the greatest mathematicians of the Middle Ages. He is perhaps most famous for the Fibonacci sequence, which can be applied to many diverse problems. One amusing application of the Fibonacci sequence is in finding the growth rate of rabbits. Suppose a pair of rabbits matures in 2 months and is capable of reproducing another pair every month after maturity. If every new pair has the same capability, how many pairs will there be after 1 year? (We assume here that no pairs die.) The table below shows the sequence for the first 7 months. Notice that at the end of the second month, the first pair matures and bears its first offspring in the third month, making the total two pairs. Month No. Number of Pairs 1 1 2 1 3 2 4 3 5 5 6 8 7 13 The Nth Fibonacci number in the sequence can be evaluated with the formula FN 1 5 1 2 5 N 1 2 5 N Write an application that accepts N and displays FN. Note that the result of computation using the Math class is double. You need to display it as an integer. 20. According to Newton’s universal law of gravitation, the force F between two bodies with masses M1 and M2 is computed as F k M d 1M 2 2 where d is the distance between the two bodies and k is a positive real number called the gravitational constant. The gravitational constant k is approximately equal to 6.67E-8 dyn cm2 /g2 . Write an application that 140 Chapter 3 Numerical Data wu23399_ch03.qxd 12/13/06 17:39 Page 140
  • 168. (1) accepts the mass for two bodies in grams and the distance between the two bodies in centimeters and (2) computes the force F. Use the standard input and output, and format the output appropriately. For your information, the force between the Earth and the Moon is 1.984E25 dyn. The mass of the earth is 5.983E27 g, the mass of the moon is 7.347E25 g, and the distance between the two is 3.844E10 cm. 21. Dr. Caffeine’s Law of Program Readability states that the degree of program readability R (whose unit is mocha) is determined as R k C V T 3 2 where k is Ms. Latte’s constant, C is the number of lines in the program that contain comments, T is the time spent (in minutes) by the programmer developing the program, and V is the number of lines in the program that contain nondescriptive variable names. Write an application to compute the program readability R. Ms. Latte’s constant is 2.5E2 mocha lines2 /min2 . (Note: This is just for fun. Develop your own law, using various functions from the Math class.) 22. If the population of a country grows according to the formula y cekx where y is the population after x years from the reference year, then we can determine the population of a country for a given year from two census figures. For example, given that a country with a population of 1,000,000 in 1970 grows to 2,000,000 by 1990, we can predict the country’s population in the year 2000. Here’s how we do the computation. Letting x be the number of years after 1970, we obtain the constant c as 1,000,000 because 1,000,000 cek0 c Then we determine the value of k as y 1,000,000ekx 2 1 , , 0 0 0 0 0 0 , , 0 0 0 0 0 0 e20k k 2 1 0 ln 2 1 , , 0 0 0 0 0 0 , , 0 0 0 0 0 0 0.03466 Finally we can predict the population in the year 2000 by substituting 0.03466 for k and 30 for x (2000 1970 30). Thus, we predict y 1,000,000e0.03466(30) 2,828,651 as the population of the country for the year 2000. Write an application that accepts five input values—year A, population in year A, year B, population in year B, and year C—and predict the population for year C. Exercises 141 wu23399_ch03.qxd 12/13/06 17:39 Page 141
  • 169. 23. In Section 3.9, we use the formula MR A 1 R 2 to derive the monthly interest rate from a given annual interest rate, where MR is the monthly interest rate and AR is the annual interest rate (expressed in a fractional value such as 0.083). This annual interest rate AR is called the stated annual interest rate to distinguish it from the effective annual interest rate, which is the true cost of a loan. If the stated annual interest rate is 9 percent, for example, then the effective annual interest rate is actually 9.38 percent. Naturally, the rate that the financial institutions advertise more prominently is the stated interest rate. The loan calculator program in Section 3.9 treats the annual interest rate that the user enters as the stated annual interest rate. If the input is the effective annual interest rate, then we compute the monthly rate as MR (1 EAR)112 1 where EAR is the effective annual interest rate. The difference between the stated and effective annual interest rates is negligible only when the loan amount is small or the loan period is short. Modify the loan calculator program so that the interest rate that the user enters is treated as the effective annual interest rate. Run the original and modified loan calculator programs, and compare the differences in the monthly and total payments. Use loan amounts of 1, 10, and 50 million dollars with loan periods of 10, 20, and 30 years and annual interest rates of 0.07, 0.10, and 0.18 percent, respectively. Try other combinations also. Visit several websites that provide a loan calculator for computing a monthly mortgage payment (one such site is the financial page at www.cnn.com). Compare your results to the values computed by the websites you visited. Determine whether the websites treat the input annual interest rate as stated or effective. Development Exercises For the following exercises, use the incremental development methodology to implement the program. For each exercise, identify the program tasks, create a design document with class descriptions, and draw the program diagram. Map out the development steps at the start. State any assumptions you must make about the input. Present any design alternatives and justify your selection. Be sure to perform adequate testing at the end of each development step. 24. Develop an application that reads a purchase price and an amount tendered and then displays the change in dollars, quarters, dimes, nickels, and 142 Chapter 3 Numerical Data wu23399_ch03.qxd 12/13/06 17:39 Page 142
  • 170. pennies. Two input values are entered in cents, for example, 3480 for $34.80 and 70 for $0.70. Display the output in the following format: Notice the input values are to be entered in cents (int data type), but the echo printed values must be displayed with decimal points (float data type). 25. MyJava Coffee Outlet runs a catalog business. It sells only one type of coffee beans, harvested exclusively in the remote area of Irian Jaya. The company sells the coffee in 2-lb bags only, and the price of a single 2-lb bag is $5.50. When a customer places an order, the company ships the order in boxes. The boxes come in three sizes: the large box holds 20 bags of 2 lb, the medium 10 bags, and the small 5 bags. The cost of a large box is $1.80; a medium box, $1.00; and a small box, $0.60. The order is shipped using the least number of boxes. For example, the order of 52 bags will be shipped in two boxes, one large and one small. Develop an application that computes the total cost of an order. Display the output in the following format: Number of Bags Ordered: 52 - $ 286.00 Boxes Used: 2 Large - $3.60 1 Medium - $1.00 1 Small - $0.60 Your total cost is: $ 291.20 26. Repeat Exercise 25, but this time, accept the date when the order is placed and display the expected date of arrival. The expected date of arrival is two weeks (14 days) from the date of order. The order date is entered as a single string in the MM/dd/yyyy format. For example, November 1, 2004 is entered as 11/01/2004. There will be exactly two digits each for the Purchase Price: $ 34.80 Amount Tendered: $ 40.00 Your change is: $ 5.20 5 one-dollar bill(s) 0 quarter(s) 2 dime(s) 0 nickel(s) 0 penn(y/ies) Thank you for your business. Come back soon. Exercises 143 wu23399_ch03.qxd 12/13/06 17:39 Page 143
  • 171. month and day and four digits for the year. Display the output in the following format: Number of Bags Ordered: 52 - $ 286.00 Boxes Used: 2 Large - $3.60 1 Medium - $1.00 1 Small - $0.60 Your total cost is: $ 291.20 Date of Order: November 1, 2004 Expected Date of Arrival: November 15, 2004 27. Using a Turtle object from the galapagos package, draw three rectangles. Accept the width and the length of the smallest rectangle from the user. The middle and the largest rectangles are 40 and 80 percent larger, respectively, than the smallest rectangle. The galapagos package and its documentation are available at www.drcaffeine.com. 28. Develop a program that draws a bar chart using a Turtle object. Input five int values, and draw the vertical bars that represent the entered values in the following manner: Your Turtle must draw everything shown in the diagram, including the axes and numbers. 10 5 7 12 3 144 Chapter 3 Numerical Data wu23399_ch03.qxd 12/13/06 17:39 Page 144
  • 172. Defining Your Own Classes—Part 1 O b j e c t i v e s After you have read and studied this chapter,you should be able to • Define a class with multiple methods and data members. • Differentiate the local and instance variables. • Define and use value-returning methods. • Distinguish private and public methods. • Distinguish private and public data members. • Pass both primitive data and objects to a method. 145 4 wu23399_ch04.qxd 12/13/06 17:59 Page 145
  • 173. 146 Chapter 4 Defining Your Own Classes—Part 1 I n t r o d u c t i o n o far we have been using only standard classes such as System, String, and others when we wrote programs. For a basic program, that is fine. However, we need to to learn how to write programs using our own classes (in addition to using the stan- dard classes) when the programs become large and complex. In this chapter, we learn the basics of how to define our own classes. And, in Chapter 7, we will cover more advanced topics on defining classes. 4.1 First Example: Defining and Using a Class The most economical and effective means of on-campus transportation is without doubt a bicycle. Suppose we want to develop a program that tracks the bicycles by assigning to them some form of identification number along with the relevant information, such the owner’s name and phone number. To develop such a Java program, we need to design many different types of objects. For example, we need objects to handle input, output, data storage, and other computational tasks. Among the many types of objects necessary for this program, we will design a core class that models a bicycle. There’s no such Bicycle class among the standard classes, of course, so we need to define one ourselves. We will learn how to define the Bicycle class in this section. We will start with a very simplistic Bicycle class. Using this class, we can only assign and retrieve the owner’s name. Before we look inside the Bicycle class and explain how the class is defined, let’s first look at how we might use it in our program. The following sample program creates two Bicycle objects, assigns the owners’ names to them, and displays the information: class BicycleRegistration { public static void main(String[] args) { Bicycle bike1, bike2; String owner1, owner2; bike1 = new Bicycle( ); //Create and assign values to bike1 bike1.setOwnerName(Adam Smith); bike2 = new Bicycle( ); //Create and assign values to bike2 bike2.setOwnerName(Ben Jones); //Output the information owner1 = bike1.getOwnerName( ); owner2 = bike2.getOwnerName( ); System.out.println(owner1 + owns a bicycle.); System.out.println(owner2 + also owns a bicycle.); } } S wu23399_ch04.qxd 12/13/06 17:59 Page 146
  • 174. The dependency diagram between the two classes is as follows: When this program is executed, we get the following output on the standard output window: Adam Smith owns a bicycle. Ben Jones also owns a bicycle. This main class should look very familiar to all of us. The key difference lies in the use of the Bicycle class instead of the standard classes we have been using so far. The way we use the Bicycle class is the same. For example, we create a Bicycle object bike2 by calling the new operator, and we assign the name of its owner by executing bike2 = new Bicycle( ); bike2.setOwnerName(Ben Jones); BicycleRegistration Bicycle Here’s the definition of the Bicycle class. To distinguish it from the standard classes, we call the Bicycle and other classes we define programmer-defined classes. 4.1 First Example:Defining and Using a Class 147 programmer- defined classes class Bicycle { // Data Member private String ownerName; //Constructor: Initialzes the data member public Bicycle( ) { ownerName = Unknown; } //Returns the name of this bicycle's owner public String getOwnerName( ) { return ownerName; } //Assigns the name of this bicycle's owner public void setOwnerName(String name) { ownerName = name: } } wu23399_ch04.qxd 12/13/06 17:59 Page 147
  • 175. To get the name of the owner of bike2, we write bike2.getOwnerName() And we can assign the returned value to a variable if we write String owner2; ... owner2 = bike2.getOwnerName(); Although it is not a requirement, we will save one class definition per file to keep things simple. For the file name, we will use the name of the class followed by the java suffix. So, we save the Bicycle class in a file named Bicycle.java. 148 Chapter 4 Defining Your Own Classes—Part 1 Save one class definition per file.Use the name of the class followed by the suffix java as the file name.Follow this rule to avoid any unnecessary complications. For this sample program, we have created two classes—BicycleRegistration (the main class) and Bicycle. So there are two source files for this program. The Bicycle Class Now let’s study the Bicycle class. Table 4.1 lists the three methods of the Bicycle class and their description. Here’s a template for the Bicycle class declaration: class Bicycle { //data members //methods } BicycleRegistration.java Bicycle.java Table Table 4.1 The three methods of the Bicycle class.The first method is called a constructor Method Parameter Description Bicycle None Initializes the owner’s name to Unassigned. getOwnerName None Returns the owner’s name. setOwnerName Name of the Assigns the bicycle owner’s name to the passed owner (string) value. wu23399_ch04.qxd 12/13/06 18:00 Page 148
  • 176. The class declaration begins with the reserved word class followed by the name. Any valid identifier that is not a reserved word can be used as the class name. We define the three methods inside the class declaration. But before we can provide the method definitions, we need to consider the data members of the Bicycle class. Remember, in Section 1.3, we stated that data members of a class are the data values associated with the class or instances of the class, such as the current balance of an Account object. What would be the data members of Bicycle objects? We need to know the owner’s name of every Bicycle object, so we’ll define one data member to store the owner’s name. The data members of a class are declared within the class declaration. Here’s how we define the data member ownerName of the Bicycle class: class Bicycle { private String ownerName; //definitions for the constructor, //getOwnerName, and setOwnerName methods come here } The ownerName data member is an instance variable (we will learn how to de- clare class constants later in this chapter and class variables in Chap. 7). Remember that, in Section 1.3, we defined an instance variable as the data member we associate to an individual instance and whose value can change over time. In other words, each instance of the class will have its own copy. After the two Bicycle objects are created and assigned their respective names by program, we have the following memory state: The syntax for the data member declaration is modifier-list data type name ; where modifier-list designates different characteristics of the data member, data type the class name or primitive data type, and name the name of the data mem- ber. Here’s how the general syntax corresponds to the actual declaration: Modifier private ownerName ; Data Type String Name bike1 “Adam Smith” :Bicycle ownerName bike2 “Ben Jones” :Bicycle ownerName 4.1 First Example:Defining and Using a Class 149 wu23399_ch04.qxd 12/13/06 18:00 Page 149
  • 177. In this example, the data member has one modifier named private. This modifier is called an accessibility modifier, or a visibility modifier, and it restricts who can have a direct access to the data member. If the modifier is private, then only the methods defined in the class can access it directly. We will provide a more detailed discus- sion of the accessibility modifiers in Section 4.6. For now, it suffices to remember that data members are declared private for the most part. Now that the necessary data member is taken care of, we are ready to define the three methods. We start with the setOwnerName method, which is declared as public void setOwnerName(String name) { ownerName = name; } The syntax for defining a method, as given in Chapter 2, is modifiers return type method name ( parameters ) { statements } The following diagram shows how the components in the general syntax cor- respond to the actual elements in the setOwnerName method: We explained in Chapter 1 that methods may or may not return a value. A method that does not return a value, such as this setOwnerName method, is declared as void. It is called a void method. The accessibility modifier for the setOwnerName method is declared as public. This means the program that uses the Bicycle class can access, or call, this method. It is possible (and could be useful) to declare a method as private. If a method is declared as private, then it cannot be called from the pro- gram that uses the class. It can only be called from the other methods of the same class. For now we will limit our discussion to public methods. Here we declare all methods as public because we want the programs that use the Bicycle class to be able to call them. We will go over the use of private methods later in the chapter. Modifier public } setOwnerName ( String name ) { Statements Return Type void Method Name Parameter ownerName = name; This refers to instance variable ownerName. This refers to parameter name. 150 Chapter 4 Defining Your Own Classes—Part 1 accessibility modifier void method wu23399_ch04.qxd 12/13/06 18:00 Page 150
  • 178. The getOwnerName method is defined as follows: public String getOwnerName( ) { return ownerName; } The following diagram shows how the components in the general syntax corre- spond to the actual elements in the getOwnerName method: This is a value-returning method. When this method is called, it returns a value to the caller. The getOwnerName method returns a string value—the value of instance variable ownerName—so its return type is declared as String. A value- returning method must include a return statement of the format return expression ; The data type of expression must be compatible with the declared return type of the method. For example, if the return type is int, then the data type of the returned value must be compatible with int (data types int, short, and byte are all compatible with int). Data type compatibilites are explained in Section 3.2. If a method returns a value, then we can include a call to the method in an ex- pression itself. For example, instead of writing Bicycle bike; ... String owner = bike.getOwnerName( ); System.out.println(owner + owns a bike.); we can write Bicycle bike; ... System.out.println(bike.getOwnerName( ) + owns a bike.); Modifier public } getOwnerName ( ) { Statements Return Type String Method Name Parameter return ownerName; This refers to instance variable ownerName. 4.1 First Example:Defining and Using a Class 151 value-returning method return state- ment syntax wu23399_ch04.qxd 12/13/06 18:00 Page 151
  • 179. A method that returns information about an object (such as who is the owner of a bicycle) is called an accessor. The getOwnerName method is an accessor. An inverse of an accessor that sets a property of an object is called a mutator. The setOwnerName method is a mutator. Accessors and mutators are commonly called get and set methods, respectively. A value-returning method can include more than one return statement. The use of multiple return statements make sense only in the context of the control statements, which we will discuss in Chapters 5 and 6. We will be seeing examples of multiple return statements in these chapters. The first method defined in the Bicycle class is a special method called a con- structor. A constructor is a special method that is executed when a new instance of the class is created, that is, when the new operator is called. Here’s the constructor for the Bicycle class: public Bicycle( ) { ownerName = Unassigned; } It follows the general syntax public class name ( parameters ) { statements } where class name is the name of the class to which this constructor belongs. The following diagram shows how the components in the general syntax correspond to the actual elements in the constructor of the Bicycle class: Notice that a constructor does not have a return type and, consequently, will never include a return statement. The modifier of a constructor does not have to be public, but non-public constructors are rarely used. This example shows no parame- ters, but it is very common to define a constructor with two or three parameters. We will see an example of a constructor that accepts two parameters in Section 4.5. Until then, we will define only a zero-parameter constructor. The purpose of the Bicycle constructor is to initialize the data member to a value that reflects the state to which the real name is not yet assigned. Since a constructor is executed when a new instance is created, it is the most logical place Modifier public } ( ) { Statements Class Name Bicycle Parameters ownerName = Unassigned; 152 Chapter 4 Defining Your Own Classes—Part 1 accessor mutator constructor wu23399_ch04.qxd 12/13/06 18:00 Page 152
  • 180. to initialize the data members and perform any other initialization tasks. Figure 4.1 shows a sequence of state-of-memory diagrams illustrating the effects of executing the constructor and the setOwnerName method of the Bicycle class. We stated earlier that the Bicycle class has three methods, of which one is a constructor. However, a constructor is distinct from other “regular” methods, so it is more common to state that the Bicycle class has one constructor and two methods. 4.1 First Example:Defining and Using a Class 153 Figure 4.1 A sequence of state-of-memory diagrams that illustrate the effects of executing the constructor and the setOwnerName method of the Bicycle class. bike bike “Unassigned” :Bicycle ownerName Bicycle bike; bike = new Bicycle( ); Bicycle bike; bike “Jon Java” :Bicycle ownerName Bicycle bike; bike = new Bicycle( ); bike.setOwnerName(Jon Java); Instead of saying“a class has three methods including one constructor,”it is more common to say“a class has one constructor and two methods.”We will use the later expression in this book. wu23399_ch04.qxd 12/13/06 18:00 Page 153
  • 181. We will provide a more detailed discussion on constructors in Section 4.5. The class diagram that lists the data member, the constructor, and two meth- ods of the Bicycle class is shown in Figure 4.2. In listing the data members and methods of a class, we will use the following convention: We list the data members first, then the constructor, and finally the methods. Within each group, we list elements in alphabetical order. Keep in mind that this convention for grouping elements and ordering them within a group is for our convenience. The Java compiler does not care how we list the data members and methods. class class name { // data members // constructor // methods } 154 Chapter 4 Defining Your Own Classes—Part 1 Data Member Listing Method Listing We include the data type of an argument passed to the method. Bicycle ownerName Bicycle( ) getOwnerName( ) setOwnerName( String ) Figure 4.2 A class diagram of the Bicycle class with two methods and one data member. class listing convention The Java compiler does not care how we order the methods and data members in the class declaration.We adopt the listing convention to make the class declaration easier for us to follow. Compiling and Running BicycleRegistration Up until now, when we ran the sample programs, we simply compiled and executed the main class. That’s all we need to do because the main class is the only class we wu23399_ch04.qxd 12/13/06 18:00 Page 154
  • 182. created for the sample programs. But for this sample program, we have created two classes—BicycleRegistration (the main class) and Bicycle. So there are two source files for this program. From now on, we will use the name of the main class to refer the whole pro- gram. To run the BicycleRegistration program, we must first compile the two source files and then run the main class. Here are the steps we follow to run this sample program (we will illustrate the steps using the minimalist approach, see App. A): 1. Compile the Bicycle class. javac Bicycle.java 2. Compile the BicycleRegistration class. javac BicycleRegistration.java 3. Run the BicycleRegistration class. java BicycleRegistration There is one last thing to remember. The way the classes are written now, the easiest way to manage a program that includes multiple programmer-defined classes is to save the source files in the same folder (directory). We will learn how to organize classes into a package in Chapter 7 so we can manage the organization of classes in a more effective manner. Until then, just remember to place all sources files for a program in the same folder. If you don’t do this, the Java compiler and interpreter may not be able to compile and run the program. BicycleRegistration.java Source files for the BicycleRegistration program Bicycle.java These files must be stored in the same folder. 4.1 First Example:Defining and Using a Class 155 Place all source files for a program in the same folder (directory). It is not necessary to create a separate folder for each program, though. In other words, one folder can contain source files for multiple programs. For example, wu23399_ch04.qxd 12/13/06 18:00 Page 155
  • 183. we could create one folder to place all source files for this chapter’s sample code. However, we recommend that students create a separate folder for each program- ming assignment or lab project for easy management. 156 Chapter 4 Defining Your Own Classes—Part 1 The class declaration can be preceded with the accessibility modifier public or private. For now, we do not use any accessibility modifier for the class dec- laration. We will discuss the issue when we discuss a package organization in Chapter 7. 1. Extend the Bicycle class by adding the second data member tagNo of type String. Declare this data member as private. 2. Add a new method to the Bicycle class that assigns a tag number. This method will be called as follows: Bicycle bike; bike = new Bicycle( ); ... bike.setTagNo(2004–134R); 3. Add a another method to the Bicycle class that returns the bicycle’s tag number. This method will be called as follows: Bicycle bike; bike = new Bicycle( ); ... String tag = bike.getTagNo( ); 4.2 Second Example: Defining and Using Multiple Classes Let’s write a second sample program to get more practice in defining classes. In this example, we will define a new class named Account. An Account object has the name of the owner (String) and the balance (double). We have two methods— add and deduct—to deposit to and withdraw money from the account. There are methods to set the initial balance and retrieve the current balance. These two methods are named setInitialBalance and getCurrentBalance. Finally, we have an accessor and mutator for the account owner’s name—getOwnerName and setOwnerName. wu23399_ch04.qxd 12/13/06 18:00 Page 156
  • 184. The second sample program uses the Bicycle class from Section 4.1 and the Account class we define shortly in this section. Here’s the second sample program: 4.2 Second Example:Defining and Using Multiple Classes 157 class SecondMain { //This sample program uses both the Bicycle and Account classes public static void main(String[] args) { Bicycle bike; Account acct; String myName = Jon Java; bike = new Bicycle( ); bike.setOwnerName(myName); acct = new Account( ); acct.setOwnerName(myName); acct.setInitialBalance(250.00); acct.add(25.00); acct.deduct(50); //Output some information System.out.println(bike.getOwnerName() + owns a bicycle and); System.out.println(has $ + acct.getCurrentBalance() + left in the bank); } } This program creates one Bicycle object and one Account object, sets their owner name to Jon Java, initializes the account balance to $250.00, adds $25.00 to the account, deducts $50.00 from the account, and finally prints out some informa- tion of bike and acct objects. The program diagram is as follows: SecondMain Bicycle Account wu23399_ch04.qxd 12/13/06 18:00 Page 157
  • 185. We are using the Bicycle class from Section 4.1 without modification, so we only have to consider defining the Account class. There are two data members for the class, one to store the owner’s name and another to maintain the account bal- ance. We have the following declaration for the two data members: class Account { private String ownerName; private double balance; //constructor and method declarations come here } The set and get methods for the owner’s name are identical to those defined for the Bicycle class. The add and deduct methods modifiy the balance by adding or deducting the passed amount. They are defined as follows: public void add(double amt) { balance = balance + amt; } public void deduct(double amt) { balance = balance - amt; } The setInitialBalance and getCurrentBalance methods are similarly defined as the other set and get methods. Here’s the complete definition of the Account class: 158 Chapter 4 Defining Your Own Classes—Part 1 class Account { // Data Members private String ownerName; private double balance; //Constructor public Account( ) { ownerName = Unassigned; balance = 0.0; } //Adds the passed amount to the balance public void add(double amt) { balance = balance + amt; } //Deducts the passed amount from the balance public void deduct(double amt) { balance = balance - amt; } wu23399_ch04.qxd 12/13/06 18:00 Page 158
  • 186. Figure 4.3 shows a class diagram of the Account class. The second sample program is composed of three classes (we are not count- ing the standard classes). We need to compile the three classes before we can run the program. However, we do not have to compile all three classes every time we want to run the program. For example, if the Bicycle class is already compiled and we are not making any changes to it, then there’s no need to compile the class again. (Note: We are assuming here that both programs are placed in the same directory. If the second program is in a separate folder, then you need to copy the bytecode file Bicycle.class to this folder.) Notice the second call to the deduct method from the main method of SecondMain, which is acct.deduct(10); but the parameter for the deduct method is declared as type double. This call is valid because we are passing a value that is assignment-compatible to the double data type. We will elaborate on this topic in Section 4.3. SecondMain.java SecondMain Program Bicycle.java Account.java 4.2 Second Example:Defining and Using Multiple Classes 159 //Returns the current balance of this account public double getCurrentBalance( ) { return balance; } //Returns the name of this account's owner public String getOwnerName( ) { return ownerName; } //Sets the initial balance of this account public void setInitialBalance(double bal) { balance = bal; } //Assigns the name of this account's owner public void setOwnerName(String name) { ownerName = name; } } wu23399_ch04.qxd 12/13/06 18:00 Page 159
  • 187. 160 Chapter 4 Defining Your Own Classes—Part 1 Figure 4.3 A class diagram of the Account class with two data members,one constructor,and six methods. Account balance ownerName Account( ) add( double ) deduct( double ) getCurrentBalance( ) getOwnerName( ) setInitialBalance( double ) setOwnerName( String ) 1. What is the output from the following code fragment? Account acct; acct = new Account( ); acct.setInitialBalance(250); acct.add(20); System.out.println(Balance: + acct.getCurrentBalance()); 2. Write a code fragment to declare and create two Account objects named acc1 and acct2. Initialize the balance to $300 and $500, respectively. Set the name of owner for both accounts to John Doe. 4.3 Matching Arguments and Parameters Consider the following sample class that includes a method named compute. This method has three parameters—two int and one double. class Demo { ... public void compute(int i, int j, double x) { //method body //the actual statements in the body //are irrelevant to the discussion } ... } wu23399_ch04.qxd 12/13/06 18:00 Page 160
  • 188. When we call the compute method, we must pass three values. The values we pass must be assignment-compatible with the corresponding parameters. For exam- ple, it is not okay to pass a double value to an int parameter. Here are some valid calls from the main method: class MyMain { public static void main(String[] arg) { Demo demo = new Demo(); int i, k, m; i = 12; k = 10; m = 14; demo.compute(3, 4, 5.5); demo.compute(i, k, m); demo.compute(m, 20, 40); } } In the statement demo.compute(m, 20, 40); the values m, 20, and 40 are called arguments. An argument is a value we pass to a method, and the value is assigned to the corresponding parameters. A parameter is a placeholder in the called method to hold the value of a passed argument. The ar- guments and parameters are matched in left-to-right order. As long as the data type of an argument is assignment-compatible to the corresponding parameter, the call is valid. The identifier we use for an argument has no relation to the identifier used for the corresponding parameter. In the statement demo.compute(i, k, m); the fact that the same identifier i is used for both the first parameter and the first ar- gument has no significance. They are two distinct and separate variables, as shown in Figure 4.4. The figure also shows how the matching is done. 4.3 Matching Arguments and Parameters 161 argument parameter A parameter receives the value of a corresponding argument.Because a parameter is like a placeholder that will not hold a value until an argument is passed to it,a parameter is called a formal parameter and an argument an actual parameter. wu23399_ch04.qxd 12/13/06 18:00 Page 161
  • 189. 4.4 Passing Objects to a Method When calling the methods of the Bicycle and Account classes, we passed a numer- ical value or a String. In this section, we study how to pass an object when calling a method. Since a String is an object, in a sense, we actually know to pass an ob- ject as an argument to a method. However, a String is treated much as a primitive datum for the most part, so we will cover this topic using instances of our own class. First, we define the Student class. A Student object has a name (String) and an email (String). Here’s the definition: 162 Chapter 4 Defining Your Own Classes—Part 1 class Demo { public void compute(int i, int j, double x) { ... } } Demo demo = new Demo( ); int i = 5; int k = 14; demo.compute( i, k, 20 ); Passing side Receiving side 5 i Passing side Receiving side Memory Allocation 14 k 20 5 i 14 j 20.0 x This is a literal constant so it has no name. Figure 4.4 This diagram illustrates how the argument values are assigned,or passed,to the matching parameters. class Student { //Data Members private String name; private string email; wu23399_ch04.qxd 12/13/06 18:00 Page 162
  • 190. Then we define the LibraryCard class. A LibraryCard object is owned by a Student, and it records the number of books being checked out. Here’s the definition: 4.4 Passing Objects to a Method 163 //Constructor public Student( ) { name = Unassigned; email = Unassigned; } //Returns the email of this student public String getEmail( ) { return email; } //Returns the name of this student public String getName( ) { return name; } //Assigns the email of this student public void setEmail(String address) { email = address; } //Assigns the name of this student public void setName(String studentName) { name = studentName; } } Student name email Student( ) getEmail( ) getName( ) setEmail( String ) setName( String ) class LibraryCard { // Data Members //student owner of this card private Student owner; //number of books borrowed private int borrowCnt; //Constructor public LibraryCard( ) { owner = null; borrowCnt = 0; } LibraryCard owner borrowCnt LibraryCard( ) checkOut( int ) getNumberOfBooks( ) getOwnerName( ) setOwner( Student ) toString( ) wu23399_ch04.qxd 12/13/06 18:00 Page 163
  • 191. Notice that we initialize the data member owner to null in the constructor. The value of null means that owner is pointing to no object. The setOwner method must be called to assign a Student object. The method accepts a Student object as its para- meter and sets the data member owner to this Student object. The getOwnerName method returns the name of the owner. It is defined as public String getOwnerName( ) { return owner.getName( ); } Because the data member owner refers to a Student object, we can get the name of this student by calling its getName method. The toString method is a method that returns a string representation of an ob- ject. Because an object can have a nested structure (e.g., an object’s data member points to an instance of another class, the data members of this instance point to instances of other classes, and so forth), it is convenient for those who use the class to have a quick way to get printable information of an instance. Without such a toString method, the programmer who uses the class must write a code to fetch the 164 Chapter 4 Defining Your Own Classes—Part 1 //numOfBooks are checked out public void checkOut(int numOfBooks) { borrowCnt = borrowCnt + numOfBooks; } //Returns the number of books borrowed public int getNumberOfBooks( ) { return borrowCnt; } //Returns the name of the owner of this card public String getOwnerName( ) { return owner.getName( ); } //Sets owner of this card to student public void setOwner(Student student) { owner = student; } //Returns the string representation of this card public String toString( ) { return Owner Name: + owner.getName( ) + n + Email: + owner.getEmail( ) + n + Books Borrowed: + borrowCnt; } } wu23399_ch04.qxd 12/13/06 18:00 Page 164
  • 192. values of the data members individually. This can be quite tedious. With the toString method, she can display information of an instance by calling just one method toString. The power of being able to pass an object to a method comes in handy when we want multiple objects to share the same object. For example, suppose a single student owns two library cards (say, one for the general library and another for the engineering library). Then we can make the data member owner of two LibraryCard objects to refer to the same Student object. Here’s one such program: 4.4 Passing Objects to a Method 165 class Librarian { public static void main(String[] args) { Student student; LibraryCard card1, card2; student = new Student( ); student.setName(Jon Java); student.setEmail([email protected]); card1 = new LibraryCard( ); card1.setOwner(student); card1.checkOut(3); card2 = new LibraryCard( ); card2.setOwner(student); //the same student is the owner //of the second card, too System.out.println(Card1 Info:); System.out.println(card1.toString() + n); System.out.println(Card2 Info:); System.out.println(card2.toString() + n); } } In this program, we create one Student object. Then we create two LibraryCard objects. For each of these LibraryCard objects, we pass the same student when call- ing their setOwner methods: card1.setOwner(student); ... card2.setOwner(student); After the setOwner method of card2 is called in the main method, we have the state of memory as shown in Figure 4.5. wu23399_ch04.qxd 1/12/07 10:43 Page 165
  • 193. It is critical to realize that when we say pass an object to a method, we are not sending a copy of an object, but rather a reference to the object. Figure 4.6 shows how the passing of an object is done. 166 Chapter 4 Defining Your Own Classes—Part 1 Figure 4.5 The state where the data members of two objects (of LibraryCard) are pointing to the same object (of Student). card1 :LibraryCard student owner 3 borrowCnt card2 :LibraryCard owner 0 borrowCnt “Jon Java” :Student name “[email protected]” email When we pass an object to a method,we are actually passing the address, or reference,of an object to the method. It is possible to return the Student object itself by defining the following method: public Student getOwner( ) { return owner; } We will discuss such a method that returns an instance of a programmer-defined class in Chapter 7. wu23399_ch04.qxd 12/13/06 18:00 Page 166
  • 194. 4.5 Constructors We provide more detailed coverage of the constructors in this section. The con- structors we have defined so far accept no arguments. These constructors set the data members to some initial values. For example, the constructor for the Bicycle class in Section 4.1 initializes the value of owner (String) to Unassigned. For this particular Bicycle class, such a simplistic constructor is adequate. However, most cases require the constructors that accept one or more arguments. In fact, the way we defined the constructor for the Account class in Section 4.2 could lead to poten- tial problems. In this section, we describe the use of constructors that accept one or more arguments, and we show how this solves the potential problems of the Account class. Let’s begin by reviewing the Account class from Section 4.2. We will identify some potential problems and present a new constructor as a solution to rectify them. 4.5 Constructors 167 Figure 4.6 This diagram illustrates how an object is passed as an argument to a method. class LibraryCard { public void setOwner(Student student) { owner = student; } } LibraryCard card2; card2 = new LibraryCard( ); card2.setOwner(student); Passing side Receiving side Memory Allocation 1 2 Passing side Receiving side For an object, the content of a variable is an address, and this address is passed to the method. 1 2 student student :LibraryCard owner 0 borrowCnt “Jon Java” :Student name “[email protected]” email card2 wu23399_ch04.qxd 12/13/06 18:00 Page 167
  • 195. Consider the following code: Account acct; acct = new Account( ); acct.setInitialBalance(500); acct.setInitialBalance(300); What is the effect of such code? It is logically inconsistent to initialize the starting balance more than once. It should be called exactly one, but there is no such Java language feature that puts constraints on the number of times the setInitialBalance method can be called. The existence of this method is a problem, and we can remove it from the Account class by defining a constructor that sets the initial balance to a specified amount. Now consider the following code: Account acct; acct = new Account( ); acct.add(200.00); If an account can have the initial balance of zero, this code is acceptable. But if there isarulethatsays,forexample,anaccountmusthavetheinitialbalanceof$25ormore, then the setInitialBalance method must be called first to initialize the balance to 25.00 or more before any transactions (add or deduct) take place. This problem can also be solved by the same constructor that sets the initial balance to a specified amount. Here’s a new constructor that eliminates the two problems in one stroke: public Account(double startingBalance) { ownerName = Unassigned; balance = startingBalance; } Once this constructor is defined, there is no longer a need for the setInitialBalance method, so we can safely remove it from the class defintion. Only the add and deduct methods affect the balance after an object is created. After the old constructor is replaced by this new constructor, we must create an instance by passing one argument when calling the new operator. For example, the code Account acct; acct = new Account(500.00); will create a new Account object with its starting balance set to $500. We can no longer create an instance by writing Account acct; acct = new Account( ); because there is no matching constructor anymore. 168 Chapter 4 Defining Your Own Classes—Part 1 wu23399_ch04.qxd 12/13/06 18:00 Page 168
  • 196. Instead of this one-parameter constructor, we can define a constructor that accepts the name of the owner also, so that it, too, can be initialized at the time of object creation. Here’s how we define the two-parameter constructor: public Account(String name, double startingBalance) { ownerName = name; balance = startingBalance; } This is the constructor we will include in the modified Account class. With this two- parameter constructor, here’s how we create an Account object: Account acct; acct = new Account(John Smith, 500.00); Notice that, even with this new constructor, we will keep the setOwnerName method in the class because we want to be able to change the name of the owner after the account is created. From the three different constructors possible for the Account class, we have selected the two-parameter constructor to include in the class. Actually, it is possi- ble to include all three constructors in the definition of the Account class. But until we learn how to define multiple constructors in Chapter 7, we will define exactly one constructor for our programmer-defined classes. 4.5 Constructors 169 It is possible to define more than one constructor to a class. Multiple contructors are called overloaded constructors. It is almost always a good idea to define multi- ple constructors to a class. But to keep things simple, we will manage with one constructor per class until Chapter 7. We are now ready to list the complete definition. Here’s the second version of the Account class (for the actual class name we will use AccountVer2 to avoid con- fusion when discussing different versions of the class definition): class AccountVer2 { // Data Members private String ownerName; private double balance; //Constructor public AccountVer2(String name, double startingBalance) { wu23399_ch04.qxd 12/13/06 18:00 Page 169
  • 197. Default Constructor As a design guideline, we strongly recommend to include constructors to programmer-defined classes, as we have been doing from the beginning of the chapter. However, it is not a requirement to define a constructor explicitly in a class. If no constructor is defined for a class, then the Java compiler will auto- matically include a default constructor. A default constructor is a constructor that accepts no arguments and has no statements in its body. For example, if we omit a constructor from the Bicycle class, a default constructor public Bicycle( ) { } will be added to the class by the compiler to ensure its instances can be created. Even though a default constructor is automatically added by the compiler, we should never rely on it. We should always define our own constructor so that we can 170 Chapter 4 Defining Your Own Classes—Part 1 ownerName = name; balance = startingBalance; } //Adds the passed amount to the balance public void add(double amt) { balance = balance + amt; } //Deducts the passed amount from the balance public void deduct(double amt) { balance = balance - amt; } //Returns the current balance of this account public double getCurrentBalance( ) { return balance; } //Returns the name of this account's owner public String getOwnerName( ) { return ownerName; } //Assigns the name of this account's owner public void setOwnerName(String name) { ownerName = name; } } default constructor wu23399_ch04.qxd 12/13/06 18:00 Page 170
  • 198. initialize the data members properly and carry out any other initialization tasks. This ensures an object is created in a valid state (such as setting the balance of an account to more than the minimum). 4.5 Constructors 171 Always define a constructor and initialize data members fully in the constructor so an object will be created in a valid state. Once we define our own constructor, no default constructor is added. This means that once the constructor, such as public Account(String name, double startingBalance ) { ownerName = name; balance = startingBalance; } is added to the Account class, we will no longer be able to create a Account object anymore by executing Account acct; acct = new Account( ); because no matching constructor can be found in the class. Once a programmer has added an explicitly defined constructor to a class,no default constructor will be added to the class by the compiler. 1. Which of the following constructors are invalid? public int ClassA(int one) { ... } public ClassB(int one, int two) { ... } void ClassC( ) { ... } wu23399_ch04.qxd 12/13/06 18:00 Page 171
  • 199. 2. What is the main purpose of a constructor? 3. Complete the following constructor. class Test { private double score; public Test(double val) { //assign the value of parameter to //the data member } } 4.6 Information Hiding and Visibility Modifiers The modifiers public and private designate the accessibility, or visibility, of data members and methods. Although it is valid in Java, we do not recommend that pro- grammers, especially beginners, leave out the visibility modifier in declaring data members and methods. From the object-oriented design standpoint, we recommend that you always designate the data members and methods as private or public. We explain how to use these modifiers in this section. But before we get into the details, we first discuss the object-oriented design philosophy behind these modifiers. Consider a mobile robot as an example. What kind of behavior do we expect from a mobile robot? Behaviors such as moving forward, turning, stopping, and changing speed come to mind easily. When we define a class, say, MobileRobot, we will include public methods such as move, turn, stop, and changeSpeed. These methods are declared public so the programmers who use a MobileRobot object can call these methods from their programs. We call these programmers client program- mers and their programs client programs. Now let’s assume that the move method accepts an integer argument as a dis- tance to travel in meters. Suppose this mobile robot has three wheels with a motor attached to each of the left and right rear wheels. The robot has no steering mecha- nism, so the turning is done by rotating the left and right rear wheels at different speeds. For example, by rotating the left wheel faster than the right wheel, the robot will make a gradual left turn. To move forward, the robot must send the same amount of power to the two motors. While the motors are rotating, the robot must constantly monitor the distance traveled and stop the motors when the designated distance is traveled. The MobileRobot class includes methods such as rotate to rotate the motor and readDistance to read the distance traveled. These methods are declared private because they are internal details that need to be hidden from the client program- mers. From our perspective as a client programmer, all we care is that the mobile robot exhibits the behavior of moving the desired distance when we call its move method. We do not care what’s going on inside. This is called information hiding. It is not our concern how many motors the robot has or what type of mechanism is employed to move the robot. We say the mobile robot encapsulates the internal workings. 172 Chapter 4 Defining Your Own Classes—Part 1 client programmers information hiding encapsulation wu23399_ch04.qxd 12/13/06 18:00 Page 172
  • 200. This encapsulation mechanism allows easier modification of program code. For example, suppose the motion mechanism of a mobile robot is modified to a sin- gle motor and rack-and-pinion steering. Both wheels are now connected to a single axle, and the motor turns this axle (via gears). The internal mechanism has changed, but this will not affect the client programs. Calling the move method still exhibits the same behavior. To implement its methods (both public and private), the MobileRobot class will necessarily include many data members, such as current speed, current direc- tion, power levels of the motors, and so forth. These data members are internal de- tails of the class because it is not a concern of the client programmers to know which and how many of them are defined in the class. As such, data members are declared as private. In summary, behavior of the instances is implemented by public methods, while the internal details that must be hidden from the client programmers are im- plemented by private methods and private data members. 4.6 Information Hiding and Visibility Modifiers 173 Public methods of a class determine the behavior of its instances.Internal details are implemented by private methods and private data members. Now let’s go through a concrete example to see what would happen if some- thing that should be an internal detail is declared public. To illustrate why declaring data members public is considered a bad design, let’s consider the AccountVer2 class. Suppose its data member balance is declared as public: class AccountVer2 { public double balance; //the rest is the same } Moving a mobile robot forward in reality is actually a far more difficult task than described in the text. First, applying the same power to the two motors does not guarantee the straight movement due to the difference in the motor characteris- tics and the floor condition.Second,the robot needs to carry out some form of ob- stacle avoidance, using a device such as a sonar or infrared sensor, because we normally do not want a robot to crash into a wall. Third, stopping is not achieved by abruptly shutting off the power to the motors. This will make the stopping too sudden. We want to gradually reduce the power level so the robot comes to a smooth stop. And there are other complexities involved in actually moving a physical robot. wu23399_ch04.qxd 12/13/06 18:00 Page 173
  • 201. If this were the class definition, we could not prohibit client programmers from writing code such as AccountVer2 myAcct; myAcct = new AccountVer2(John Smith, 300.00); myAcct.balance = 670.00; This breaks the AccountVer2 class because the balance can be modified directly by the client programmers. The purpose of removing the setInitialBalance method is defeated because the client programmers will have direct access to the data member balance. They can change its value as they wish. If the instance variable balance is properly hidden by declaring it private, then the client programmers cannot modify its value directly. They can update the value indirectly only via the add and deduct meth- ods. This maintains the integrity of the class, because the values of the data members are changed only via the public methods the class designer provides. The client pro- grammers cannot access or modify the data members through the back door. 174 Chapter 4 Defining Your Own Classes—Part 1 Declaring the data members private ensures the integrity of the class. To distingush the private and public components of a class in the program di- agram, we use the plus symbol () for public and the minus symbol () for private. Using these symbols, the diagram that shows both data members and methods for the AccountVer2 class becomes AccountVer2 balance ownerName AccountVer2( String, double ) add( double ) deduct( double ) getCurrentBalance( ) getOwnerName( ) setOwnerName( String ) 1. If the data member speed is private, is the following statement valid in a client program? Robot aibo; aibo = new Robot(); double currentSpeed = aibo.speed; wu23399_ch04.qxd 12/13/06 18:00 Page 174
  • 202. 2. Suppose you wrote down important information, such as your bank account number, student registration ID, and so forth, on a single sheet of paper. Will this sheet be declared private and kept in your desk drawer, or public and placed next to the dorm’s public telephone? 3. Identify the private methods from the following diagram. 4.7 Class Constants We introduced the use of the reserved final in declaring constants in Section 3.3. The constants we declared there were used by only one method—the main method. In this section we will show how a class constant is declared. A class constant will be shared by all methods of the class. Let’s define another version of the Account class (the actual name will be AccountVer3). This time we will charge a fixed fee whenever a deduction is made. Here’s how the class is declared (we will not list the unchanged methods here): MyClass mydata : double MyClass( ) methodOne(double) : void methodTwo(double) : double methodThree(double) : double 4.7 Class Constants 175 class AccountVer3 { // Data Members private static final double FEE = 0.50; private String ownerName; private double balance; //Constructor public AccountVer3(String name, double startingBalance) { ownerName = name; balance = startingBalance; } //Deducts the passed amount from the balance public void deduct(double amt) { balance = balance - amt - FEE; } Class constant declaration Fee is charged every time wu23399_ch04.qxd 12/13/06 18:00 Page 175
  • 203. This is the output we get when we run the program: Owner: Carl Smith Bal : $18.50 Notice the use of a DecimalFormat object to display the result to two decimal places. Here is the dependency relationship diagram (standard classes are not included) DeductionWithFee AccountVer3 176 Chapter 4 Defining Your Own Classes—Part 1 //other methods are exactly the same as before, so //we will omit them here } import java.text.*; class DeductionWithFee { //This sample program deducts money three times //from the account public static void main(String[] args) { DecimalFormat df = new DecimalFormat(0.00); AccountVer3 acct; acct = new AccountVer3(Carl Smith, 50.00); acct.deduct(10); acct.deduct(10); acct.deduct(10); System.out.println(Owner: + acct.getOwnerName()); System.out.println(Bal : $ + df.format(acct.getCurrentBalance())); } } The following sample program shows that the fee of $1.50 is charged after three deductions. wu23399_ch04.qxd 12/13/06 18:00 Page 176
  • 204. Bad Version and the source files for the program are The class constant FEE is declared as private static final double FEE = 0.50; The modifier final designates that the identifier FEE is a constant, and the modifier static designates that it is a class constant. The reserved word static is used to de- clare class components, such as class variables and class methods. The inclusion of the reserved word static in the declaration of the main method indicates that it is a class method. It is not so frequent that we use class variables and class methods (except, of course, the main method), and we will not be seeing their examples until later in the book. Before we move to another example, consider the following (problematic) declaration: class AccountVer3 { private final double FEE = 0.50; //the rest is the same } This declaration is not an error, but it is inefficient. If FEE is declared as a class con- stant, then there will be one copy for the class, and this single copy is shared by all in- stances of the class. If FEE is declared without the static modifier, then it is an instance constant. This means every instance of the class will have its own copy of the same value. For example, instead of one copy of the value 0.50, there will be 100 copies of the same value 0.50 if there are 100 instances of the class. So, to make effective use of a memory, when we declare a data member as a constant, it should be declared as a class constant. This problem was introduced in Chapter 1, and Figure 1.9 illustrates the problem. DeductionWithFee.java AccountVer3.java 4.7 Class Constants 177 If a data member is a constant,declare it as a class constant. wu23399_ch04.qxd 12/13/06 18:00 Page 177
  • 205. Let’s try another sample program. This time we will write a class that models a die. Notice how the constants are used in the following Die class: 178 Chapter 4 Defining Your Own Classes—Part 1 class Die { //Data Members //the largest number on a die private static final int MAX_NUMBER = 6; //the smallest number on a die private static final int MIN_NUMBER = 1; //To represent a die that is not yet rolled private static final int NO_NUMBER = 0; private int number; //Constructor public Die( ) { number = NO_NUMBER; } //Rolls the die public void roll( ) { number = (int) (Math.floor(Math.random() * (MAX_NUMBER - MIN_NUMBER + 1)) + MIN_NUMBER); } //Returns the number on this die public int getNumber( ) { return number; } } We use the instance variable number to store the value of a die after it is rolled. Inside the constructor, we initialize number to the constant NO_NUMBER to repre- sent the state before the die is rolled. The roll method uses the formula for random number generation described in Chapter 3. The minimum and the maximum numbers on a die are kept as the class constants. By changing their values, our soft- ware die can be made to represent any range of values, not just 1 to 6. (Note: Yes, we can change their values when we edit the class. A Java constant only means that we cannot change its value while the program is running.) wu23399_ch04.qxd 12/13/06 18:00 Page 178
  • 206. Here’s a program that uses three Die objects to simulate a roll of three dice: 4.7 Class Constants 179 class RollDice { //Simulates the rolling of three dice public static void main(String[] args) { Die one, two, three; one = new Die( ); two = new Die( ); three = new Die( ); one.roll(); two.roll(); three.roll(); System.out.println(Results are + one.getNumber( ) + + two.getNumber( ) + + three.getNumber( ) ); } } The dependency diagram and a sample output are as follows: Results are 3 6 5 The output of this program is rather primitive, but it still conveys the neces- sary information. We will learn some drawing techniques in Chapter 5, so we can really draw the image of three dice. Let’s adapt the implemention of the Die class to write another program. Here’s the scenario for our next program. Getting a single-occupancy room in a dormitory is very tough because of high demand. There’s one especially large and comfortable single-occupancy room in your dorm that everybody covets. The housing office runs a lottery at the beginning of a quarter. Students must submit RollDice Die wu23399_ch04.qxd 12/13/06 18:00 Page 179
  • 207. their entries before the lottery (if there’s no winner, then the room will be auc- tioned off at eBay). The result of the lottery will consist of three cards. The num- bers on a card range from 10 to 15, and the color of a card can be red, green, or blue. Here are some possible outcomes: We will write a program that will select a winning combination of lottery cards. Following the implementation style of the Die class, we will define a class that models the lottery card. There will be two instance variables, one for color and another for the number. We will use a random number generator to select a color and a number for each lottery card. To represent a color, we will use a simple coding: 1 for red, 2 for green, and 3 for blue. Here’s the LotteryCard class: 13 13 13 draw 1 draw 2 13 10 15 12 11 10 draw 3 180 Chapter 4 Defining Your Own Classes—Part 1 class LotteryCard { // Data Members //the largest number on a card private static final int MAX_NUMBER = 15; //the smallest number on a card private static final int MIN_NUMBER = 10; //to represent a card before drawing private static final int NO_NUMBER = 0; //the 'largest' color for a card private static final int MAX_COLOR = 3; //the 'smallest' color for a card private static final int MIN_COLOR = 1; //to represent a card before drawing private static final int NO_COLOR = 0; //selected number on this card private int number; //selected color of this card private int color; wu23399_ch04.qxd 12/13/06 18:00 Page 180
  • 208. 4.7 Class Constants 181 //Constructor public LotteryCard( ) { number = NO_NUMBER; color = NO_COLOR; } //spin the card public void spin( ) { number = (int) (Math.floor(Math.random() * (MAX_NUMBER - MIN_NUMBER + 1)) + MIN_NUMBER); color = (int) (Math.floor(Math.random() * (MAX_COLOR - MIN_COLOR + 1)) + MIN_COLOR); } //Returns the number on this card public int getNumber( ) { return number; } //Returns the color of this card public int getColor( ) { return color; } } And here’s the main class that draws the winning card combination: class RoomWinner { //Simulates the rolling of three dice public static void main(String[] args) { LotteryCard one, two, three; one = new LotteryCard( ); two = new LotteryCard( ); three = new LotteryCard( ); one.spin(); two.spin(); three.spin(); System.out.println(Winning Card Combination: ); System.out.println(1 - red; 2 - green; 3 - blue); System.out.println( ); wu23399_ch04.qxd 12/13/06 18:00 Page 181
  • 209. The dependency diagram is as follows: When this program is executed, output similar to the following is displayed: Winning Card Combination: 1 - red; 2 - green; 3 - blue Color number Card 1: 2 13 Card 2: 2 12 Card 3: 1 14 Again, the output is rather primitive. We will learn some drawing techniques in Chapter 5 so we can draw the image of a card in the appropriate color. Public Constants We stated in Section 4.6 that data members should be declared private to ensure the integrity of a class. Following this guideline, we declared the class constant data members in both sample programs as private. But there is an exception. We may want to declare certain types of class constants as public. Here are the reasons for this exception. First, a constant is “read only” by its nature, so it won’t have a neg- ative impact if we declare it as public. Second, a constant is a clean way to make certain characteristics of the instances known to the client programs. RoomWinner LotteryCard 182 Chapter 4 Defining Your Own Classes—Part 1 System.out.println( color number); System.out.println(Card 1: + one.getColor( ) + + one.getNumber( )); System.out.println(Card 2: + two.getColor( ) + + two.getNumber( )); System.out.println(Card 3: + three.getColor( ) + + three.getNumber( )); } } wu23399_ch04.qxd 12/13/06 18:00 Page 182
  • 210. For example, if we want to make the amount of a fee public knowledge (which is a good idea, because consumers need to know such information), we make the class constant public as follows: class AccountVer3 { public static final double FEE = 0.50; ... } A client program can then access this information directly as System.out.println(Fee charged per deduction is $ + AccountVer3.FEE); Notice that the class data members are accessed by the syntax class name . class data members The use of public class constants is quite common in Java, and we will be seeing many examples of it in the later sample programs. 4.8 Local Variables 183 1. Declare two class constants named MIN_BALANCE and MAX_BALANCE whose data types are double. 2. Is there any problem with the following declarations? class Question { private final int MAX = 20; ... } 3. Modify the Die class so its instances will generate a number between 5 and 15, inclusively. 4.8 Local Variables We often need to use temporary variables while completing a task in a method. Consider the deduct method of the Account class: public void deduct(double amt) { balance = balance - amt; } We can rewrite the method, using a local variable, as follows: public void deduct(double amt) { double newBalance; This is a local variable wu23399_ch04.qxd 12/13/06 18:00 Page 183
  • 211. newBalance = balance - amt; balance = newBalance; } The variable newBalance is called a local variable. They are declared within the method declaration and used for temporary purposes, such as storing intermedi- ate results of a computation. Such two-step assignment to update the current balance may not seem so use- ful here, but consider a situation in which we need to check for certain conditions before actually changing the value of currentBalance. For example, we may want to disallow the purchase if the balance goes below a preset minimum balance. So if newBalance becomes lower than the set minimum, then we’ll leave balance un- changed. If we don’t use any local variable, then we have to deduct the amount from balance (temporarily) and change it back to the previous amount. Use of a tempo- rary local variable will result in a much cleaner code. We will see how such check- ing is done in Chapter 5 using a selection statement. The methods in the sample classes from this chapter are still very short, so the useoflocalvariablesmaynotbeclear-cut.However,wewillwitnessanincreaseinthe use of local variables in the coming chapters when the methods become complex. While the data members of a class are accessible from all instance methods of the class, local variables and parameters are accessible only from the method in which they are declared, and they are available only while the method is being executed. Memory space for local variables and parameters is allocated upon declaration and at the beginning of the method, respectively, and erased upon exiting from the method. 184 Chapter 4 Defining Your Own Classes—Part 1 local variable Local variables and parameters are erased when the execution of a method is completed. When you declare a local variable, make sure the identifier you use for it does not conflict with the data members of a class. Consider the following hypothetical class declaration: class Sample { private int number; ... public void doSomething( ) { int number; number = 15; } ... } The same identifier is used for both the local variable and the instance variable. This changes the value of the local variable,not the instance variable. wu23399_ch04.qxd 12/13/06 18:00 Page 184
  • 212. This class declaration is not an error. It is acceptable to use the same identifier for a local variable, but it is not advisable. The following association rules are used: 4.9 Calling Methods of the Same Class 185 Rules for associating an identifier to a local variable,a parameter,and a data member: 1. If there’s a matching local variable declaration or a parameter,then the identifier refers to the local variable or the parameter. 2. Otherwise,if there’s a matching data member declaration,then the identifier refers to the data member. 3. Otherwise,it is an error because there’s no matching declaration. So the assignment number = 15; will change the value of the local variable. This may or may not be the intent of the programmer. Even if this is the programmer’s intention, it is cleaner and easier to read, especially to other programmers, to use different identifiers for local variables. Avoid using the same identifier for the local variables and the data members of a class. 1. How is a local variable different from an instance variable? 2. Rewrite the following method, using local variables. public int totalCharge(int amt) { return (balance - (int) Math.round(amt * 1.5)); } 4.9 Calling Methods of the Same Class Up until now, whenever we called a method of some object, we used dot notation, such as acct.deduct(12). Just as we can call a method of another object, it is possi- ble to call a method from a method of the same object. Figure 4.7 illustrates the wu23399_ch04.qxd 12/13/06 18:00 Page 185
  • 213. difference between calling another method of the same object and calling a method of a different object. Let’s look at a few examples. In the first example, we modify the AccountVer3 class so the add and deduct methods call the private method adjust. Here’s how the modified class is declared (the actual class name is AccountVer4, and only the rele- vant portion is listed here): 186 Chapter 4 Defining Your Own Classes—Part 1 :AClass public void myMethod(){ BClass obj = new BClass(); obj.doWork(); } :BClass public void doWork(){ ... } public void myMethod(){ doWork(); } Dot notation is optional when you are calling a method of the same object. Dot notation is necessary when you are calling a method of another object. Figure 4.7 The difference between calling a method belonging to the same object and a method belonging to a different object. class AccountVer4 { ... //Adds the passed amount to the balance public void add(double amt) { adjust(amt); } //Deducts the passed amount from the balance public void deduct(double amt) { adjust( -(amt+FEE) ); } ... //Adjusts the account balance private void adjust(double adjustAmt) { balance = balance + adjustAmt; } } wu23399_ch04.qxd 12/13/06 18:00 Page 186
  • 214. The add and deduct methods differ only in whether you add to or subtract the amount from the balance. In the modified class, we redefine the two methods so they call the common private method adjust. This method adds the passed amount to the balance (in the case for the deduct method, we pass the negative amount be- cause adding a negative value X is equivalent to subtracting a positive value X). Here’s how the add method is defined: public void add(double amt) { adjust(amt); } Notice there is no dot notation.This is calling another method that belongs to the same class. When we call a method that belongs to the same class, we just include the method name, as follows: adjust(amt); No dot notation is necessary. 4.9 Calling Methods of the Same Class 187 No dot notation is necessary when you call a method from another method of the same class. Let’s look at the second example. In the original Die class, when a new in- stance was created, we set its number to NO_NUMBER. This means if a programmer calls the getNumber method of a Die object before calling its roll method, she will get NO_NUMBER as a result. For a real die, there’s no such NO_NUMBER state, so instead of instantiating a new Die object in such a state, we’ll redefine the class so a die gets rolled when it is first created. The trick here is to call the roll method from the constructor. Here’s how the modified Die class is declared (the class name is DieVer2): class DieVer2 { //Data Members //the largest number on a die private static final int MAX_NUMBER = 6; wu23399_ch04.qxd 12/13/06 18:00 Page 187
  • 215. The constructor simply calls the roll method. So when a new Die object is cre- ated, a number is already preselected. Notice that it is possible to declare the constructor as public DieVer2( ) { number = (int) (Math.floor(Math.random() * (MAX_NUMBER - MIN_NUMBER + 1)) + MIN_NUMBER); } But this ends up duplicating the same code. Instead of repeating the same code in the class, it is much better organizationally to define a single method and call this method from multiple places. Duplication of code, in general, makes the modifica- tion of code tedious and error-prone. Imagine the situation in which the same code is repeated at 10 different locations. It is so easy to miss one or two of them at the modification time. 188 Chapter 4 Defining Your Own Classes—Part 1 //the smallest number on a die private static final int MIN_NUMBER = 1; private int number; //Constructor public DieVer2( ) { roll(); } //Rolls the die public void roll( ) { number = (int) (Math.floor(Math.random() * (MAX_NUMBER - MIN_NUMBER + 1)) + MIN_NUMBER); } //Returns the number on this die public int getNumber( ) { return number; } } Avoid duplicating the same code.Duplication of code often means tedious and error-prone activities when you modify the code. wu23399_ch04.qxd 12/13/06 18:00 Page 188
  • 216. 4.10 Changing Any Class to a Main Class In this section, we will show you a simple way to make any class (such as Bicycle) also the main class of a program. Instead of defining a separate main class, as we have done so far, it is possible to define the main method to a class so the class be- comes the main class of a program also. There are a number of advantages in doing this. First, we have one less class to manage if we don’t have to define a separate main class. This advantage may not be seem so substantial. However, when we write numerous classes (e.g., writing solutions to the chapter exercises), writing a separate main class for all those classes so they become executable becomes te- dious. Second, when we develop reusable classes (such as Die and Account) for other programmers, we often want to include a simple example on how to use the classes. Instead of providing a separate sample main class, it is more convenient to add the main method to these classes. We illustrate the procedure, using the Bicycle class from Section 4.1. Suppose we want to show a sample use of this class. Instead of creating a separate sample main class, we can define the main method to the Bicycle class. Here’s the Bicycle class that is also a main class: class Bicycle { // Data Member private String ownerName; //Returns the name of this bicycle's owner public String getOwnerName( ) { return ownerName; } //Assigns the name of this bicycle's owner public void setOwnerName(String name) { ownerName = name; } //The main method that shows a sample //use of the Bicycle class public static void main(String[] args) { Bicycle myBike; myBike = new Bicycle( ); 4.10 Changing Any Class to a Main Class 189 1. Suppose a class Alpha includes a method called compute that accepts no arguments. Define another method of Alpha named myMethod that calls the compute method. 2. Why should duplication of code be avoided? wu23399_ch04.qxd 12/13/06 18:00 Page 189
  • 217. myBike.setOwnerName(Jon Java); System.out.println(myBike.getOwnerName() + owns a bicycle); } } Remember that the new Bicycle class having the main method does not pro- hibit us from defining a separate main class. All Java requires us to do is to include the main method to the classes we designate as the main class of the program. So it is possible (although not likely) that every class in the program has the main method, and we can select one of them to be the main class when we execute the program. We will use this technique whenever appropriate in the textbook, begin- ning with this chapter’s sample development section. 190 Chapter 4 Defining Your Own Classes—Part 1 Any class can include the main method.For a program to be executable,the desig- nated main class must include the main method.Other classes in the program may or may not include the main method.It is irrelevant to the execution of the program. program tasks Loan Calculator In Chapter 3, we wrote a loan calculator program that computes the monthly and total payments for a given loan amount, loan period, and interest rate.We wrote the program using the simplified program structure in which we had one main class with one method (main). We will implement the program again, but this time we use classes called Loan and LoanCalculator. Problem Statement The problem statement is the same as that in Chapter 3. We repeat the statement to refresh your memory: Write a loan calculator program that computes both monthly and total pay- ments for a given loan amount,annual interest rate,and loan period. Overall Plan The tasks we identified in Chapter 3 for the program are still the same: 1. Get three input values:loanAmount,interestRate, and loanPeriod. 2. Compute the monthly and total payments. 3. Output the results. Sample Development 4.11 Sample Development wu23399_ch04.qxd 12/13/06 18:00 Page 190
  • 218. The main difference in this implementation lies in the use of additional classes. In- stead of building the program by using only the main class and performing all the tasks in one big main method, we will define two classes Loan and LoanCalculator. An instance of the LoanCalculator class acts as a top-level agent that manages all other objects in the program, such as Loan and Scanner. The Loan class captures the logic of loan calculation. A single instance of the Loan class represents a loan, so if the program deals with five loans, for example, then five Loan objects will be created in the program. We will make the LoanCalculator class the main class of the program by adding the main method to it.Figure 4.8 shows the program diagram. Notice that the roles that LoanCalculator and Loan play in the program are quite different.The Loan class is a generic class that provides a service (i.e.,loan computation and currency conversion) and is intended to be reused by different programs. The LoanCalculator class, on the other hand, is a class designed specifically for this pro- gram, so the class is not intended for reuse by other programs. It is important to recog- nize this distinction because the way we design reusable and nonreusable classes is quite different. We call the class that provides some type of service a service provider and the class that manages other classes and objects in a program a controller. In gen- eral, a service provider is designed as a reusable class, while a controller is designed as a nonreusable class. What would be the development steps for this program? If we have multiple classes to implement, we can develop the program in either a top-down or a bottom- up manner. With the top-down development, we develop in a kind of outside-in fashion. We develop the top-level controller class first. But to test its functionalities fully, we need the service objects it uses. In a top-down development, we use tempo- rary dummy service objects that return a fake value from their methods. After we verify that the controller class is working correctly, we then complete the service class with the real methods. The top-down development for this program will implement the LoanCalculator class first with the dummy Loan class and then the real Loan class. 4.11 Sample Development 191 Scanner LoanCalculator Loan Figure 4.8 The program diagram for the LoanCalculator program. service provider controller top-down develop- ment wu23399_ch04.qxd 12/13/06 18:00 Page 191
  • 219. 4.11 Sample Development—continued 192 Chapter 4 Defining Your Own Classes—Part 1 develop- ment steps step 1 design With the bottom-up development, we develop in the reverse inside-out fashion; that is, we develop the service classes first.To test the service classes, we write a tempo- rary dummy main class.After the service classes are done,we complete the top-level class that uses these service classes.The bottom-up development for this program implements the Loan class first fully and then the LoanCalculator class. For both approaches, the classes are developed incrementally as usual. For this sample development, we will adopt the top-down development. We will leave the bottom-up development for this program as an exercise. For some sample ap- plications in the later chapters,we will adopt the bottom-up development.We implement this program in five steps: 1. Start a skeleton of the LoanCalculator class.The skeleton LoanCalculator class will include only an object/variable declaration and a constructor to create objects.Define a temporary placeholder Loan class. 2. Implement the input routine to accept three input values. 3. Implement the output routine to display the results. 4. Implement the computation routine to compute the monthly and total payments. 5. Finalize the program,implementing any remaining temporary methods and adding necessary methods as appropriate. Step 1 Development: Program Skeleton Since the LoanCalculator object is the top-level agent of the program that manages other objects,we need a method to create these objects.We do this in the constructor.We define separate methods for input, computation, and output to organize the class more logically. Designing a set of single-task methods is more manageable and easier to un- derstand than having one method that performs all three tasks of input, computation, and output.We will call the methods getInput, computePayment, and displayOutput. We will also include one method called describeProgram that describes the purpose of the program to the user. Since an instance of the class is the top-level agent, much as a general contractor, we will provide one method the programmer can call to control the whole operation.We will name the method start and define it as follows: public void start( ) { describeProgram(); getInput(); computerPayment(); displayOutput(); } bottom-up develop- ment wu23399_ch04.qxd 12/13/06 18:00 Page 192
  • 220. With this method,we can then call the main method as follows: public static void main(String[] args){ LoanCalculator calculator = new LoanCalculator(); calculator.start(); } It is possible to define the main method to make it call the four operation methods (describeProgram, computePayment, getInput, and displayOutput) directly, elimi- nating the need to define the start method. However, such organization limits the flexi- bility and usability of the class.By defining the start method, if other programmers want to use the LoanCalculator class in their programs, they need only call the start method. Without the start method,they have to call the four methods and remember to call them in the correct order. Although the difference is not dramatic in this particular case, it can be in the cases when the classes are more complex and the number of classes in a pro- gram is large. Let’s summarize the methods we will define for the LoanCalculator class: 4.11 Sample Development 193 Design Document: The LoanCalculator Class Method Visibility Purpose start public Carries out the loan calculation by calling the other private methods. computePayment private Given three parameters—loan amount, loan period, and interest rate—it com- putes monthly and total payments.The actual computation is done by a Loan object. describeProgram private Displays a short description of the program. displayOutput private Displays the result—monthly and total payments. getInput private Uses Scanner to get three input values—loan amount, loan period, and interest rate. Notice that only the start method is public. We declare all other methods as private because we do not want any client programmers to use them directly;we want the client programmers to be able to call only the start method. In this step, we define the four private methods with only a temporary output statement inside the method body to verify that the methods are called correctly. A method that has no “real” statements inside the method body is called a stub. The four stub wu23399_ch04.qxd 12/13/06 18:00 Page 193
  • 221. 4.11 Sample Development—continued 194 Chapter 4 Defining Your Own Classes—Part 1 methods are defined as follows: private void describeProgram() { System.out.println(inside describeProgram); //TEMP } private void getInput() { System.out.println(inside getInput); //TEMP } private void computePayment() { System.out.println(inside computePayment); //TEMP } private void displayOutput() { System.out.println(inside displayOutput); //TEMP } Notice the comment marker //TEMP after the output statements. It is our convention to attach this comment marker so we can easily and quickly locate temporary statements. We use System.out for temporary output. The purpose of the skeleton LoanCalculator class is to declare and create all the necessary data members. At this step, we know of only one object that will be used by LoanCalculator, namely,a Loan object.The declaration part of the LoanCalculator class will be as follows: class LoanCalculator { private Loan loan; ... } At this point, the constructor for the LoanCalculator class is very simple.The only data member is a Loan object,so we will create it in the constructor as follows: public LoanCalculator( ) { loan = new Loan( ); } For this constructor to work properly,we need the definition for the Loan class.We begin with the minimalist skeleton code for the Loan class: class Loan { public Loan( ) { } } wu23399_ch04.qxd 12/13/06 18:00 Page 194
  • 222. Let’s put our design in an actual code.The skeleton LoanCalculator class is defined as follows. 4.11 Sample Development 195 step 1 code /* Chapter 4 Sample Development: Loan Calculation (Step 1) File: Step1/LoanCalculator.java */ class LoanCalculator { //Data members private Loan loan; //Main method public static void main(String[] arg) { LoanCalculator calculator = new LoanCalculator(); calculator.start(); } //Constructor public LoanCalculator() { loan = new Loan(); } // Top-level method that calls other private methods public void start() { describeProgram(); //tell what the program does getInput(); //get three input values computePayment(); //compute the monthly payment and total displayOutput(); //display the results } // Computes the monthly and total loan payments private void computePayment() { System.out.println(inside computePayment); //TEMP } // Provides a brief explanation of the program to the user private void describeProgram() { System.out.println(inside describeProgram); //TEMP } // Displays the input values and monthly and total payments private void displayOutput() { System.out.println(inside displayOutput); //TEMP } wu23399_ch04.qxd 12/13/06 18:00 Page 195
  • 223. 4.11 Sample Development—continued 196 Chapter 4 Defining Your Own Classes—Part 1 // Gets three input values—loan amount, interest rate, and // loan period—using an InputBox object private void getInput() { System.out.println(inside getInput); //TEMP } } And finally the skeleton Loan class is defined as follows. We run the step 1 program and verify that the following text appears in the standard out- put window: inside describeProgram inside getInput inside computePayment inside displayOutput After the step 1 program is compiled and executed correctly, we move on to step 2. Step 2 Development: Accept Input Values In the second step of coding,we implement the getInput method.We will reuse the input routine we derived in Chapter 3.When we receive three input values,we must pass these values to the Loan object loan.We will add three data members to keep track of the three step 2 design step 1 test /* Chapter 4 Sample Development: Loan Calculation (Step 1) File: Step1/Loan.java */ class Loan { public Loan( ) { } } wu23399_ch04.qxd 12/13/06 18:00 Page 196
  • 224. input values and one constant to aid the conversion: class Loan { private static final int MONTHS_IN_YEAR = 12; private double loanAmount; private double monthlyInterestRate; private int numberOfPayments; ... } Notice that the annual interest rate and loan period expressed in the number of years are the input,but we are keeping monthly interest rate and the number of monthly payments for the loan period to make them more compatible to the loan calculation formula we are using.We need to define three set methods (mutators) for interest rate, loan period, and loan amount. A set method for the number of payments, for example, can be defined as follows: public void setPeriod(int periodInYear) { numberOfPayments = periodInYear * MONTHS_IN_YEAR; } We define a complementary set of accessor methods. The getPeriod method, for example,is defined as public int getPeriod( ) { return (numberOfPayments / MONTHS_IN_YEAR); } Notice that the value returned by an accessor may or may not be the data member. It is possible that the value returned is derived from the data member, as was the case with the getLoanPeriod method. We mentioned in Section 4.4 the importance of a constructor initializing an object properly.Now that we have associated data members to the Loan class,let’s define a con- structor that accepts arguments: public Loan(double amount, double rate, int period) { setAmount(amount); setRate (rate ); setPeriod(period); } Having this updated Loan class, we are now ready to tackle the getInput method of the LoanCalculator class.We perform the input routine as we did in the sample pro- gram from Chapter 3: Scanner scanner = new Scanner(System.in); System.out.print(Loan Amount (Dollars+Cents): ); loanAmount = scanner.nextDouble(); 4.11 Sample Development 197 wu23399_ch04.qxd 12/13/06 18:00 Page 197
  • 225. 4.11 Sample Development—continued 198 Chapter 4 Defining Your Own Classes—Part 1 System.out.print(Annual Interest Rate (e.g., 9.5): ); annualInterestRate = scanner.nextDouble(); System.out.print(Loan Period - # of years: ); loanPeriod = scanner.nextInt(); After getting three input values,we create a new Loan object as loan = new Loan(loanAmount, annualInterestRate, loanPeriod); Finally, we include test output statements to verify that the values are read in and assigned to loan correctly: System.out.println(Loan Amount: $ + loan.getAmount()); System.out.println(Annual Interest Rate: + loan.getRate() + %); System.out.println(Loan Period (years): + loan.getPeriod()); From this point on, to maintain a focus on the changes we are making, we show only the portion where we made modifications or additions. Unchanged portions are represented by three dots (. . .). Please refer to the actual source file for the viewing of complete source code.Here’s the step 2 LoanCalculator class: step 2 code /* Chapter 4 Sample Development: Loan Calculation (Step 2) File: Step2/LoanCalculator.java */ import java.util.*; class LoanCalculator { . . . public LoanCalculator() { } . . . private void getInput(){ double loanAmount, annualInterestRate; wu23399_ch04.qxd 12/13/06 18:00 Page 198
  • 226. 4.11 Sample Development 199 The step 2 Loan class is as follows: int loanPeriod; Scanner scanner = new Scanner(System.in); System.out.print(Loan Amount (Dollars+Cents):); loanAmount = scanner.nextDouble(); System.out.print(Annual Interest Rate (e.g., 9.5):); annualInterestRate = scanner.nextDouble(); System.out.print(Loan Period - # of years:); loanPeriod = scanner.nextInt(); //create a new loan with the input values loan = new Loan(loanAmount, annualInterestRate,loanPeriod); //TEMP System.out.println(Loan Amount: $ + loan.getAmount()); System.out.println(Annual Interest Rate: + loan.getRate() + %); System.out.println(Loan Period (years): + loan.getPeriod()); //TEMP } . . . } /* Chapter 4 Sample Development: Loan Calculation (Step 2) File: Step2/Loan.java */ class Loan { private final int MONTHS_IN_YEAR = 12; private double loanAmount; private double monthlyInterestRate; private int numberOfPayments; wu23399_ch04.qxd 12/13/06 18:00 Page 199
  • 227. 4.11 Sample Development—continued 200 Chapter 4 Defining Your Own Classes—Part 1 //Constructor public Loan(double amount, double rate, int period) { setAmount(amount); setRate (rate ); setPeriod(period); } //Returns the loan amount public double getAmount( ) { return loanAmount; } //Returns the loan period in number of years public int getPeriod( ) { return numberOfPayments / MONTHS_IN_YEAR; } //Returns the loan's annual interest rate public double getRate( ) { return monthlyInterestRate * 100.0 * MONTHS_IN_YEAR; } //Sets the loan amount public void setAmount(double amount) { loanAmount = amount; } //Sets the annual interest rate public void setRate(double annualRate) { monthlyInterestRate = annualRate / 100.0 / MONTHS_IN_YEAR; } //Sets the loan period public void setPeriod(int periodInYears) { numberOfPayments = periodInYears * MONTHS_IN_YEAR; } } As before, to verify the input routine is working correctly, we run the program multiple times. For each run, we enter a different set of data to verify that the values entered are displayed correctly. step 2 test wu23399_ch04.qxd 12/13/06 18:01 Page 200
  • 228. Step 3 Development: Output Values In the third step of development, we implement the displayOutput method. We will reuse the design of output layout from Chapter 3. The actual task of computing the monthly and total payments is now delegated to the Loan class, so we will add two methods—getMonthlyPayment and getTotalPayment—to the Loan class.The focus in step 3 is the layout for output, so we will define a temporary dummy code for these two methods in the following manner: public double getMonthlyPayment( ) { return 132.15; //TEMP } public double getTotalPayment( ) { return 15858.10; //TEMP } To display the monthly and total payments, we add the following code in the displayOutput method: private void displayOutput( ) { //echo print the input values here System.out.println(Monthly payment is $ + loan.getMonthlyPayment() ); System.out.println( TOTAL payment is $ + loan.getTotalPayment() ); } Notice that by defining the getMonthlyPayment and getTotalPayment methods in the Loan class, the computePayment method of LoanCalculator becomes redundant and no longer needed.So we will remove it in this step. Here are the modified LoanCalculator and Loan classes: 4.11 Sample Development 201 step 3 design step 3 code /* Chapter 4 Sample Development: Loan Calculation (Step 3) File: Step3/LoanCalculator.java */ import java.util.*; class LoanCalculator { ... // computePayment method is removed from the source file private void displayOutput() { System.out.println(Loan Amount: $ + loan.getAmount()); wu23399_ch04.qxd 12/13/06 18:01 Page 201
  • 229. 4.11 Sample Development—continued 202 Chapter 4 Defining Your Own Classes—Part 1 System.out.println(Annual Interest Rate: + loan.getRate() + %); System.out.println(Loan Period (years): + loan.getPeriod()); System.out.println(Monthly payment is $ + loan.getMonthlyPayment()); System.out.println( TOTAL payment is $ + loan.getTotalPayment()); } private void getInput() { //same code but the temporary echo print statements //are removed } } /* Chapter 4 Sample Development: Loan Calculation (Step 3) File: Step3/Loan.java */ class Loan { ... public double getMonthlyPayment( ) { return 132.15; //TEMP } public double getTotalPayment( ) { return 15858.10; //TEMP } ... } To verify the output routine is working correctly, we run the program multiple times and verify that the layout looks okay for different values.It is common for a programmer to run the program several times before the layout looks clean on the screen. step 3 test wu23399_ch04.qxd 12/13/06 18:01 Page 202
  • 230. Bad Version Step 4 Development: Compute Loan Amount In the fourth step of development,we replace the temporary getMonthlyPayment and getTotalPayment methods with the final version. The changes are made only to the Loan class.The other two classes remain the same. Here’s one possible way to define the two methods: private double monthlyPayment; public double getMonthlyPayment( ) { monthlyPayment = ...; return monthlyPayment; } public double getTotalPayment( ) { return monthlyPayment * numberOfPayments; } The idea is to use the value of the data member monthlyPayment set by the getMonthlyPayment method in computing the total payment.This setup is problematic because the getTotalPayment method will not work correctly unless getMonthly- Payment is called first. It is considered a very poor design, and generally unacceptable, to require the client programmer to call a collection of methods in a certain order. We must define the two methods so they can be called in any order,not necessarily in the order of getMonthlyPayment and getTotalPayment. The correct way here is to call getMonthlyPayment from the getTotalPayment method: private double getTotalPayment( ) { double totalPayment; totalPayment = getMonthlyPayment() * numberOfPayments; return totalPayment; } With this approach the data member monthlyPayment is not necessary. Here’s the updated Loan class: 4.11 Sample Development 203 step 4 design step 4 code /* Chapter 4 Sample Development: Loan Calculation (Step 4) File: Step4/Loan.java */ class Loan { ... public double getMonthlyPayment( ) { double monthlyPayment; wu23399_ch04.qxd 12/13/06 18:01 Page 203
  • 231. 4.11 Sample Development—continued 204 Chapter 4 Defining Your Own Classes—Part 1 After the method is added to the class, we need to run the program through a number of test data.As in Chapter 3,we made the assumption that the input values must be valid, so we will only test the program for valid input values. For sample test data, we repeat the table from Chapter 3.The right two columns show the correct results.Remem- ber that these input values are only suggestions,not a complete list of test data.You must try other input values as well. monthlyPayment = (loanAmount * monthlyInterestRate) / (1 - Math.pow(1/(1 + monthlyInterestRate), numberOfPayments ) ); return monthlyPayment; } public double getTotalPayment( ) { double totalPayment; totalPayment = getMonthlyPayment( ) * numberOfPayments; return totalPayment; } ... } step 4 test Input Output (shown up to three decimal places only) Loan Amount Annual Interest Rate Loan Period (in Years) Monthly Payment Total Payment 10000 10 10 132.151 15858.088 15000 7 15 134.824 24268.363 10000 12 10 143.471 17216.514 0 10 5 0.000 0.000 30 8.5 50 0.216 129.373 Step 5 Development: Finalize Now in the last step of development, we finalize the class declaration by completing the describeProgram method, the only method still undefined. We may give a very long step 5 design wu23399_ch04.qxd 12/13/06 18:01 Page 204
  • 232. description or a very terse one.An ideal program will let the user decide.We do not know how to write such code yet, so we will display a short description of the program using System.out. Another improvement is the display of monetary values in two decimal places. We can format the display to two decimal places by using the DecimalFormat class as explained in Chapter 3. Here’s the describeProgram method; private void describeProgram() { System.out.println (This program computes the monthly and total); System.out.println (payments for a given loan amount, annual ); System.out.println (interest rate, and loan period (# of years).); System.out.println(n); } You may feel that there’s not much testing we can do in this step. After all, we add only a single method that carries out a simple output routine. However, many things can go wrong between step 4 and step 5.You may have deleted some lines of code inadvertently. You may have deleted a necessary file by mistake.Anything could happen.The point is to test after every step of development to make sure everything is in order. Summary 205 step 5 code step 5 test • Data members of a class refer to the instance and class variables and constants of the class. • An object’s properties are maintained by a set of data members. • Class methods can access only the class variables and class constants. • Instance methods can access all types of data members of the class. • Public methods define the behavior of an object. • Private methods and data members (except certain class constants) are considered internal details of the class. • Components (data members and methods) of a class with the visibility modifier private cannot be accessed by the client programs. • Components of a class with the visibility modifier public can be accessed by the client programs. • A method may or may not return a value. One that does not return a value is called a void method. S u m m a r y wu23399_ch04.qxd 12/13/06 18:01 Page 205
  • 233. • A constructor is a special method that is executed when a new object is created. Its purpose is to initialize the object into a valid state. • Memory space for local variables and parameters is allocated when a method is called and deallocated when the method terminates. • A public method that changes a property of an object is called a mutator. • A public method that retrieves a property of an object is called an accessor. • Dot notation is not used when you call a method from another method of the same class. • Any class can be set as the main class of a program by adding the main method to it. In the main method, an instance of this class is created. 206 Chapter 4 Defining Your Own Classes—Part 1 K e y C o n c e p t s programmer-defined classes accessibility (visibility) modifiers void methods value-returning methods accessors mutators constructors arguments parameters client programmers information hiding encapsulation local variables service providers controllers stub E x e r c i s e s 1. Consider the following class declaration. class QuestionOne { public final int A = 345; public int b; private float c; private void methodOne( int a) { b = a; } public float methodTwo( ) { return 23; } } wu23399_ch04.qxd 12/13/06 18:01 Page 206
  • 234. Identify invalid statements in the following main class. For each invalid statement, state why it is invalid. class Q1Main { public static void main(String[] args) { QuestionOne q1; q1 = new QuestionOne( ); q1.A = 12; q1.b = 12; q1.c = 12; q1.methodOne(12); q1.methodOne( ); System.out.println(q1.methodTwo(12)); q1.c = q1.methodTwo( ); } } 2. What will be the output from the following code? class Q2Main { public static void main(String[] args) { QuestionTwo q2; q2 = new QuestionTwo( ); q2.init(); q2.increment(); q2.increment(); System.out.println(q2.getCount()); } } class QuestionTwo { private int count; public void init( ) { count = 1; } public void increment( ) { count = count + 1; } public int getCount( ) { return count; } } Exercises 207 wu23399_ch04.qxd 12/13/06 18:01 Page 207
  • 235. 3. What will be the output from the following code? Q3Main and Question Three classes are the slightly modified version of Q2Main and QuestionTwo. class Q3Main { public static void main(String[] args) { QuestionThree q3; q3 = new QuestionThree( ); q3.init(); q3.count = q3.increment() + q3.increment(); System.out.println(q3.increment()); } } class QuestionThree { public int count; public void init( ) { count = 1; } public int increment( ) { count = count + 1; return count; } } 4. Is there any problem with the following class? Is the passing of an argument to the private methods appropriate? Are the data members appropriate? Explain. 208 Chapter 4 Defining Your Own Classes—Part 1 /* Problem Question4 */ class MyText { private String word; private String temp; private int idx; public String firstLetter( ) { idx = 0; return getLetter(word); } public String lastLetter( ) { idx = word.length() - 1; return getLetter(word); } wu23399_ch04.qxd 12/13/06 18:01 Page 208
  • 236. 5. In the RollDice program, we created three Die objects and rolled them once. Rewrite the program so you will create only one Die object and roll it three times. 6. Modify the Bicycle class so instead of assigning the name of an owner (Student), you can assign the owner object itself. Model this new Bicycle class after the LibraryCard class. 7. Extend the LibraryCard class by adding the expiration date as a new property of a library card. Define the following four methods: //sets the expiration date public void setExpDate(GregorianCalendar date) {...} //returns the expiration year public int getExpYear( ) { ... } //returns the expiration month public int getExpMonth( ) { ... } //returns the expiration day public int getExpDay( ) { ... } 8. Write a program that displays the recommended weight (kg), given the user’s age and height (cm). The formula for calculating the recommended weight is recommendedWeight = (height - 100 + age / 10) * 0.90 Define a service class named Height and include an appropriate method for getting a recommended weight of a designated height. 9. Write a program that computes the total ticket sales of a concert. There are three types of seatings: A, B, and C. The program accepts the number of tickets sold and the price of a ticket for each of the three types of seats. The total sales are computed as follows: totalSales = numberOfA_Seats * pricePerA_Seat + numberOfB_Seats * pricePerB_Seat + numberOfC_Seats * pricePerC_Seat; Write this program, using only one class, the main class of the program. 10. Redo Exercise 9 by using a Seat class. An instance of the Seat class keeps track of the ticket price for a given type of seat (A, B, or C). Exercises 209 private String getLetter(String str) { temp = str.substring(idx, idx+1); return temp; } } wu23399_ch04.qxd 12/13/06 18:01 Page 209
  • 237. 11. Write a program that computes the area of a circular region (the shaded area in the diagram), given the radii of the inner and the outer circles, ri and ro, respectively. We compute the area of the circular region by subtracting the area of the inner circle from the area of the outer circle. Define a Circle class that has methods to compute the area and circumference. You set the circle’s radius with the setRadius method or via a constructor. 12. Write a WeightConverter class. An instance of this class is created by passing the gravity of an object relative to the Earth’s gravity (see Exercise 12 on page 138). For example, the Moon’s gravity is approximately 0.167 of the Earth’s gravity, so we create a WeightConverter instance for the Moon as WeightConverter moonWeight; moonWeight = new WeightConverter( 0.167 ); To compute how much you weigh on the Moon, you pass your weight on Earth to the convert method as yourMoonWeight = moonWeight.convert( 160 ); Use this class and redo Exercise 12 on page 138. Development Exercises For the following exercises, use the incremental development methodology to implement the program. For each exercise, identify the program tasks, create a design document with class descriptions, and draw the program diagram. Map out the development steps at the start. Present any design alternatives and justify your selection. Be sure to perform adequate testing at the end of each development step. 13. Redo Exercise 26 on page 143, but this time define and use programmer- defined classes. ri ro 210 Chapter 4 Defining Your Own Classes—Part 1 wu23399_ch04.qxd 12/13/06 18:01 Page 210
  • 238. 14. Write a program that accepts the unit weight of a bag of coffee in pounds and the number of bags sold and displays the total price of the sale, computed as follows: totalPrice = bagWeight * numberOfBags * pricePerLb; totalPriceWithTax = totalPrice + totalPrice * taxrate; Display the result in the following manner: Number of bags sold: 32 Weight per bag: 5 lb Price per pound: $5.99 Sales tax: 7.25% Total price: $ 1027.88 Define and use a programmer-defined CoffeeBag class. Include class constants for the price per pound and tax rate with the values $5.99 per pound and 7.25 percent, respectively. 15. In the Turtle exercises from the earlier chapters, we dealt with only one Turtle (e.g., see Exercise 28 on page 144). It is possible, however, to let multiple turtles draw on a single drawing window. To associate multiple turtles to a single drawing, we create an instance of TurtleDrawingWindow and add turtles to it as follows: TurtleDrawingWindow canvas = new TurtleDrawingWindow( ); Turtle winky, pinky, tinky; //create turtles; //pass Turtle.NO_DEFAULT_WINDOW as an argument so //no default drawing window is attached to a turtle. winky = new Turtle(Turtle.NO_DEFAULT_WINDOW); pinky = new Turtle(Turtle.NO_DEFAULT_WINDOW); tinky = new Turtle(Turtle.NO_DEFAULT_WINDOW); //now add turtles to the drawing window canvas.add( winky ); canvas.add( pinky ); canvas.add( tinky ); Ordinarily, when you start sending messages such as turn and move to a Turtle, it will begin moving immediately. When you have only one Turtle, this is fine. However, if you have multiple turtles and want them to start moving at the same time, you have to first pause them, then give instructions, and finally command them to start moving. Here’s the basic idea: winky.pause( ); pinky.pause( ); tinky.pause( ); Exercises 211 Format to two decimal places. wu23399_ch04.qxd 12/13/06 18:01 Page 211
  • 239. //give instructions to turtles here, //e.g., pinky.move(50); etc. //now let the turtles start moving winky.start( ); pinky.start( ); tinky.start( ); Using these Turtle objects, draw the following three triangles: Use a different pen color for each triangle. Run the same program without pausing and describe what happens. pinky draws this triangle. winky draws this triangle. tinky draws this triangle. 212 Chapter 4 Defining Your Own Classes—Part 1 wu23399_ch04.qxd 12/13/06 18:02 Page 212
  • 240. Selection Statements O b j e c t i v e s After you have read and studied this chapter,you should be able to • Implement selection control in a program using if statements. • Implement selection control in a program using switch statements. • Write boolean expressions using relational and boolean operators. • Evaluate given boolean expressions correctly. • Nest an if statement inside another if statement’s then or else part correctly. • Describe how objects are compared. • Choose the appropriate selection control statement for a given task. • Define and use enumerated constants. • Draw geometric shapes on a window. 213 5 wu23399_ch05.qxd 12/14/06 17:49 Page 213
  • 241. ecisions, decisions, decisions. From the moment we are awake until the time we go to sleep, we are making decisions. Should I eat cereal or toast? What should I wear to school today? Should I eat at the cafeteria today? And so forth. We make many of these decisions by evaluating some criteria. If the number of students in line for registration seems long, then come back tomorrow for another try. If today is Monday, Wednesday, or Friday, then eat lunch at the cafeteria. Computer programs are no different. Any practical computer program con- tains many statements that make decisions. Often a course of action is determined by evaluating some kind of a test (e.g., Is the remaining balance of a meal card below the minimum?). Statements in programs are executed in sequence, which is called sequential execution or sequential control flow. However, we can add decision-making statements to a program to alter this control flow. For example, we can add a statement that causes a portion of a program to be skipped if an input value is greater than 100. Or we can add a statement to disallow the pur- chase of food items if the balance of a meal card goes below a certain minimum. The statement that alters the control flow is called a control statement. In this chapter we describe some important control statements, called selection state- ments. In Chapter 6 we will describe other control statements, called repetition statements. 5.1 The if Statement There are two versions of the if statement, called if–then–else and if–then. We begin with the first version. Suppose we wish to enter a student’s test score and print out the message You did not pass if the score is less than 70 and You did pass if the score is 70 or higher. Here’s how we express this logic in Java: Scanner scanner = new Scanner(System.in); System.out.print(Enter test score: ); int testScore = scanner.nextInt(); if (testScore 70) System.out.println(You did not pass); else System.out.println(You did pass); We use an if statement to specify which block of code to execute. A block of code may contain zero or more statements. Which block is executed depends on the 214 Chapter 5 Selection Statements I n t r o d u c t i o n D sequential execution control statement if statement This statement is executed if testScore is less than 70. This statement is executed if testScore is 70 or higher. wu23399_ch05.qxd 12/14/06 17:49 Page 214
  • 242. result of evaluating a test condition, called a boolean expression. The if–then–else statement follows this general format: if ( boolean expression ) then block else else block Figure 5.1 illustrates the correspondence between the if–then–else statement we wrote and the general format. The boolean expression is a conditional expression that is evaluated to either true or false. For example, the following three expressions are all conditional: testScore 80 testScore * 2 350 30 w / (h * h) The six relational operators we can use in conditional expressions are: // less than = // less than or equal to == // equal to != // not equal to // greater than = // greater than or equal to Here are some more examples: a * a = c //true if a * a is less than or equal to c x + y != z //true if x + y is not equal to z a == b //true if a is equal to b 5.1 The if Statement 215 boolean expression if-then-else syntax relational operators testScore 70 ) if ( else System.out.println(You did not pass); System.out.println(You did pass); Boolean Expression The then Block The else Block Figure 5.1 Mapping of the sample if–then–else statement to the general format. wu23399_ch05.qxd 12/14/06 17:49 Page 215
  • 243. If the boolean expression evaluates to true, then the statements in the then block are executed. Otherwise, the statements in the else block are executed. We will cover more complex boolean expressions in Section 5.2. Notice that we can reverse the relational operator and switch the then and else blocks to derive the equivalent code, for example, if (testScore = 70) System.out.println(You did pass); else System.out.println(You did not pass); Notice that the reverse of is =, not . The if statement is called a selection or branching statement because it selects (or branches to) one of the alternative blocks for execution. In our example, either System.out.println(You did not pass); or System.out.println(You did pass); is executed depending on the value of the boolean expression. We can illustrate a branching path of execution with the diagram shown in Figure 5.2. 216 Chapter 5 Selection Statements One very common error in writing programs is to mix up the assignment and equality operators. We frequently make the mistake of writing if (x = 5) ... when we actually wanted to say if (x == 5) ... selection statement testScore 70? System.out.println (You did not pass); System.out.println (You did pass); true false Figure 5.2 The diagram showing the control flow of the sample if–then–else statement. wu23399_ch05.qxd 12/14/06 17:49 Page 216
  • 244. In the preceding if statement, both blocks contain only one statement. The then or else block can contain more than one statement. The general format for both the then block and the else block is either a single statement or a compound statement where single statement is a Java statement and compound statement is a sequence of Java statements surrounded by braces, as shown below with n 0 statements: { statement 1 statement 2 ... statement n } If multiple statements are needed in the then block or the else block, they must be surrounded by braces { and }. For example, suppose we want to print out addi- tional messages for each case. Let’s say we also want to print Keep up the good work when the student passes and print Try harder next time when the student fails. Here’s how: 5.1 The if Statement 217 Compound Statements if (testScore 70) { System.out.println(You did not pass); System.out.println(Try harder next time); } else { System.out.println(You did pass); System.out.println(Keep up the good work); } The braces are necessary to delineate the statements inside the block. Without the braces, the compiler will not be able to tell whether a statement is part of the block or part of the statement that follows the if statement. Notice the absence of semicolons after the right braces. A semicolon is never necessary immediately after a right brace. A compound statement may contain zero wu23399_ch05.qxd 12/14/06 17:49 Page 217
  • 245. or more statements, so it is perfectly valid for a compound statement to include only one statement. Indeed, we can write the sample if statement as if (testScore 70) { System.out.println(You did not pass); } else { System.out.println(You did pass); } Although it is not required, many programmers prefer to use the syntax for the com- pound statement even if the then or else block includes only one statement. In this textbook, we use the syntax for the compound statement regardless of the number of statements inside the then and else blocks. Following this policy is beneficial for a number of reasons. One is the ease of adding temporary output statements inside the blocks. Frequently, we want to include a temporary output statement to verify that the boolean expression is written correctly. Suppose we add output statements such as these: if (testScore 70) { System.out.println(inside then: + testScore); System.out.println(You did not pass); } else { System.out.println(inside else: + testScore); System.out.println(You did pass); } If we always use the syntax for the compound statement, we just add and delete the temporary output statements. However, if we use the syntax of the single state- ment, then we have to remember to add the braces when we want to include a temporary output statement. Another reason for using the compound statement syntax exclusively is to avoid the dangling else problem. We discuss this problem in Section 5.2. The placement of left and right braces does not matter to the compiler. The compiler will not complain if you write the earlier if statement as if (testScore 70) { System.out.println(You did not pass); System.out.println(Try harder next time);} else { System.out.println(You did pass); System.out.println(Keep up the good work);} 218 Chapter 5 Selection Statements wu23399_ch05.qxd 12/14/06 17:49 Page 218
  • 246. However, to keep your code readable and easy to follow, you should format your if statements using one of the two most common styles: if ( boolean expression ) { ... } else { ... } if ( boolean expression ) { ... } else { ... } In this book, we will use style 1, mainly because this style adheres to the code con- ventions for the Java programming language. If you prefer style 2, then go ahead and use it. Whichever style you choose, be consistent, because a consistent look and feel is very important to make your code readable. 5.1 The if Statement 219 Style 1 Style 2 The document that provides the details of code conventions for Java can be found at http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html This document describes the Java language coding standards dictated in the Java Language Specification.It is important to follow the code conventions as closely as possible to increase the readability of the software. There is a second variation of style 1 in which we place the reserved word else on a new line as if ( boolean expression ) { ... } else { ... } Many programmers prefer this variation of style 1 because the reserved word else aligns with the matching if. However, if we nitpick, style 3 goes against the logic behind the recommended style 1 format, which is to begin a new statement at one position with a reserved word. The reserved word else is a part of the if statement, not the beginning of a new statement. Thus style 1 places the reserved word else to the right of the matching if. Style 3 wu23399_ch05.qxd 12/14/06 17:49 Page 219
  • 247. Again, the actual format is not that important. Consistent use of the same for- mat is. So, whichever style you use, use it consistently. To promote consistency among all programmers, we recommend that everybody to adopt the code conven- tions. Even though the recommended format may look peculiar at first, with some repeated use, the format becomes natural in no time. Let’s summarize the key points to remember: 220 Chapter 5 Selection Statements Rules for writing the then and else blocks: 1. Left and right braces are necessary to surround the statements if the then or else block contains multiple statements. 2. Braces are not necessary if the then or else block contains only one statement. 3. A semicolon is not necessary after a right brace. Now let’s study a second version of the if statement called if–then. Suppose we want to print out the message You are an honor student if the test score is 95 or above and print out nothing otherwise. For this type of testing, we use the second version of the if statement, whose general format is if ( boolean expression ) then block The second version contains only the then block. Using this version and the compound statement syntax, we express the selection control as if (testScore = 95) { System.out.println(You are an honor student); } Figure 5.3 shows the diagram that illustrates the control flow for this if–then state- ment. We will refer collectively to both versions as the if statement. Notice that the if–then statement is not necessary, because we can write any if–then statement using if–then–else by including no statement in the else block. For instance, the sample if–then statement can be written as if (testScore = 95) { System.out.println(You are an honor student); } else { } In this book, we use if–then statements whenever appropriate. Let’s conclude this section with a sample class that models a circle. We will name the class Ch5Circle, and its instances are capable of computing the if-then syntax wu23399_ch05.qxd 12/14/06 17:49 Page 220
  • 248. circumference and area. We will include a test in this class so the methods such as getArea and getCircumference return the constant INVALID_DIMENSION when the dimension of the radius is invalid. Here’s the Ch5Circle class (most comments are removed for the sake of brevity): 5.1 The if Statement 221 Figure 5.3 The diagram showing the control flow of the second version of the if statement. System.out.println (You are an honor student); true false testScore = 95? /* Chapter 5 The Circle class File: Ch5Circle.java */ class Ch5Circle { public static final int INVALID_DIMENSION = -1; private double radius; public Ch5Circle(double r) { setRadius(r); } public double getArea( ) { double result = INVALID_DIMENSION; if (isRadiusValid()) { result = Math.PI * radius * radius; } return result; } As the number of methods gets larger, we will use this marker to quickly locate the program components.Shaded icon is used for a private element. Data Members getArea wu23399_ch05.qxd 12/14/06 17:49 Page 221
  • 249. public double getCircumference( ) { double result = INVALID_DIMENSION; if (isRadiusValid()) { result = 2.0 * Math.PI * radius; } return result; } public double getDiameter( ) { double diameter = INVALID_DIMENSION; if (isRadiusValid()) { diameter = 2.0 * radius; } return diameter; } public double getRadius( ) { return radius; } public void setDiameter(double d) { if (d 0) { setRadius(d/2.0); } else { setRadius(INVALID_DIMENSION); } } public void setRadius(double r) { if (r 0) { radius = r; } else { radius = INVALID_DIMENSION; } } private boolean isRadiusValid( ) { return radius != INVALID_DIMENSION; } } 222 Chapter 5 Selection Statements getDiameter getRadius setDiameter setRadius isRadiusValid getCircumference wu23399_ch05.qxd 12/14/06 17:49 Page 222
  • 250. Notice the if statement in the getArea method is written as if (isRadiusValid()) { ... } The boolean expression in the if statement can be any expression that evaluates to true or false, including a call to a method whose return type is boolean, such as the isRadiusValid method. The use of such a boolean method often makes the code easier to read, and easier to modify if the boolean method is called from many methods (e.g., there are three methods calling the isRadiusValid method). Here’s a short main class to test the functionality of the Ch5Circle class: 5.1 The if Statement 223 /* Chapter 5 Sample Program: Computing Circle Dimensions File: Ch5Sample1.java */ import java.util.*; class Ch5Sample1 { public static void main(String[] args) { double radius, circumference, area; Ch5Circle circle; Scanner scanner = new Scanner(System.in); System.out.print(Enter radius: ); radius = scanner.nextDouble(); circle = new Ch5Circle(radius); circumference = circle.getCircumference(); area = circle.getArea(); System.out.println(Input radius: + radius); System.out.println(Circumference: + circumference); System.out.println(Area: + area); } } wu23399_ch05.qxd 12/14/06 17:49 Page 223
  • 251. Notice that the program will display -1.0 when the input radius is invalid. We can improve the display by adding an if test in the main program as follows: System.out.print(Circumference: ); if (circumference == Ch5Circle.INVALID_DIMENSION) { System.out.println(Cannot compute. Input invalid); } else { System.out.println(circumference); } Another possible improvement in the main program is to check the input value first. For instance, radius = ... ; if (radius 0) { //do the computation as the sample main method } else { //print out the error message } Even when a client programmer does not include appropriate tests in his program, we must define a reusable class in a robust manner so it will not crash or produce erroneous results. For the Ch5Circle class, we add a test so the data member radius is set to either a valid datum or a specially designated value (INVALID_DIMENSION) for any invalid data. By designing the class in this manner, we protect the class from a possible misuse (e.g., attempting to assign a negative radius) and producing mean- ingless results, such as –5.88. We always strive for a reliable and robust reusable class that will withstand the abuse and misuse of client programmers. 224 Chapter 5 Selection Statements 1. Identify the invalid if statements: a. if ( a b ) then x = y; else x = z; b. if ( a b ) else x = y; c. if ( a b ) x = y; else { x = z; }; d. if ( a b ) { x = y; } else x = z; 2. Are the following two if statements equivalent? /*A*/ if ( x y ) System.out.println(Hello); else System.out.println(Bye); /*B*/ if ( x y ) System.out.println(Bye); else System.out.println(Hello); wu23399_ch05.qxd 12/14/06 17:49 Page 224
  • 252. 5.2 Nested if Statements The then and else blocks of an if statement can contain any statement including another if statement. An if statement that contains another if statement in either its then or else block is called a nested if statement. Let’s look at an example. In the earlier example, we printed out the messages You did pass or You did not pass depending on the test score. Let’s modify the code to print out three possible mes- sages. If the test score is lower than 70, then we print You did not pass, as before. If the test score is 70 or higher, then we will check the student’s age. If the age is less than 10, we will print You did a great job. Otherwise, we will print You did pass, as before. Figure 5.4 is a diagram showing the logic of this nested test. The code is written as follows: if (testScore = 70) { if (studentAge 10) { System.out.println(You did a great job); } else { System.out.println(You did pass);//test score = 70 } //and age = 10 } else { //test score 70 System.out.println(You did not pass); } 5.2 Nested if Statements 225 nested if statement Figure 5.4 A diagram showing the control flow of the example nested if statement. System.out.println (You did a great job); System.out.println (You did pass); true false studentAge 10? System.out.println (You did not pass); true false testScore = 70? Another if statement in the then block of the outer if wu23399_ch05.qxd 12/14/06 17:49 Page 225
  • 253. Since the then block of the outer if contains another if statement, the outer if is called a nested if statement. It is possible to write if tests in different ways to achieve the same result. For example, the preceding code can also be expressed as if (testScore = 70 studentAge 10) { System.out.println(You did a great job); } else { //either testScore 70 OR studentAge = 10 if (testScore = 70) { System.out.println(You did pass); } else { System.out.println(You did not pass); } } Several other variations can also achieve the same result.As a general rule, we strive to select the one that is most readable (i.e., most easily understood) and most efficient. Often no one variation stands out, and the one you choose depends on your preferred style of programming. Here’s an example in which one variation is clearly a better choice. Suppose we input three integers and determine how many of them are negative. Here’s the first variation. To show the structure more clearly, we purposely do not use the braces in the then and else blocks. if (num1 0) if (num2 0) if (num3 0) negativeCount = 3; //all three are negative else negativeCount = 2; //num1 and num2 are negative else if (num3 0) negativeCount = 2; //num1 and num3 are negative else negativeCount = 1; //num1 is negative else if (num2 0) if (num3 0) negativeCount = 2; //num2 and num3 are negative else negativeCount = 1; //num2 is negative else if (num3 0) negativeCount = 1; //num3 is negative else negativeCount = 0; //no negative numbers 226 Chapter 5 Selection Statements In this and the following examples,we purposely do not use the braces so we can provide a better illustration of the topics we are presenting. wu23399_ch05.qxd 12/14/06 17:49 Page 226
  • 254. It certainly did the job. But elegantly? Here’s the second variation: negativeCount = 0; if (num1 0) negativeCount = negativeCount + 1; if (num2 0) negativeCount = negativeCount + 1; if (num3 0) negativeCount = negativeCount + 1; Which version should we use? The second variation is the only reasonable way to go. The first variation is not a viable option because it is very inefficient and very difficult to read. We apply the nested if structure if we have to test conditions in some required order. In this example these three tests are independent of one an- other, so they can be executed in any order. In other words, it doesn’t matter whether we test num1 first or last. The statement negativeCount = negativeCount + 1; increments the variable by 1. This type of statement that changes the value of a vari- able by adding a fixed number occurs frequently in programs. Instead of repeating the same variable name twice, we can write it succinctly as negativeCount++; Similarly, a statement such as count = count - 1; can be written as count--; The double plus operator () is called the increment operator, and the double minus operator () is the decrement operator (which decrements the variable by 1). The increment and decrement operators have higher precedence than unary operators. See Table 5.3 on page 235. Note: There are prefix and postfix increment/ decrement operators in which the operators come before and after the variable (or an expression), respectively. We only use the postfix operators in this book, and the precedence rules presented in the table apply to the postfix operators only. Notice that we indent the then and else blocks to show the nested structure clearly. Indentation is used as a visual guide for the readers. It makes no difference to a Java compiler. For example, we make our intent clear by writing the statement as if (x y) if (z != w) a = b + 1; else a = c + 1; else a = b * c; 5.2 Nested if Statements 227 increment and decrement operators wu23399_ch05.qxd 12/14/06 17:49 Page 227
  • 255. 228 Chapter 5 Selection Statements It takes some practice before you can write well-formed if statements. Here are some rules to help you write the if statements. Rule 1: Minimize the number of nestings. Rule 2: Avoid complex boolean expressions. Make them as simple as possible. Don’t include many ANDs and ORs. Rule 3: Eliminate any unnecessary comparisons. Rule 4: Don’t be satisfied with the first correct statement. Always look for improvement. Rule 5: Read your code again.Can you follow the statement easily? If not,try to improve it. Test Score Grade 90 score A 80 score 90 B 70 score 80 C 60 score 70 D score 60 F The statement can be written as if (score = 90) System.out.println(Your grade is A); else if (score = 80) System.out.println(Your grade is B); else if (score = 70) System.out.println(Your grade is C); But to the Java compiler, it does not matter if we write the same code as if (x y)if (z != w)a = b + 1;else a = c + 1; else a = b * c; Although indentation is not required to run the program, using proper inden- tation is an important aspect of good programming style. Since the goal is to make your code readable, not to follow any one style of indentation, you are free to choose your own style. We recommend style 1 shown on page 219. The next example shows a style of indentation accepted as standard for a nested if statement in which nesting occurs only in the else block. Instead of deter- mining whether a student passes or not, we will now display a letter grade based on the following formula: wu23399_ch05.qxd 12/14/06 17:49 Page 228
  • 256. else if (score = 60) System.out.println(Your grade is D); else System.out.println(Your grade is F); However, the standard way to indent the statement is as follows: if (score = 90) System.out.println(Your grade is A); else if (score = 80) System.out.println(Your grade is B); else if (score = 70) System.out.println(Your grade is C); else if (score = 60) System.out.println(Your grade is D); else System.out.println(Your grade is F); We mentioned that indentation is meant for human eyes only. For example, we can clearly see the intent of a programmer just by looking at the indentation when we read if (x y) if (x z) System.out.println(Hello); else System.out.println(Good bye); A Java compiler, however, will interpret the above as if (x y) if (x z) System.out.println(Hello); else System.out.println(Good bye); This example has a dangling else problem. The Java compiler matches an else with the previous unmatched if, so the compiler will interpret the statement by matching the else with the inner if ( if (x z) ), whether you use indentation style A or B. If you want to express the logic of indentation style A, you have to express it as if (x y) { if (x z) System.out.println(Hello); } else System.out.println(Good bye); 5.2 Nested if Statements 229 Indentation style A Indentation style B dangling else problem wu23399_ch05.qxd 12/14/06 17:49 Page 229
  • 257. This dangling else problem is another reason why we recommend that beginners use the syntax for compound statement in the then and else blocks. In other words, always use the braces in the then and else blocks. Let’s conclude this section by including tests inside the add and deduct methods of the Account class from Chapter 4. For both methods, we will update the balance only when the amount passed is positive. Furthermore, for the deduct method, we will update the balance only if it does not become a negative number after the deduction. This will require the use of a nested if statement.The following is the class declaration. The name is Ch5Account, and this class is based on AccountVer2 from Chapter 4. We only list the two methods here because other parts are the same as in AccountVer2. 230 Chapter 5 Selection Statements class Ch5Account { ... //Adds the passed amount to the balance public void add(double amt) { //add if amt is positive; otherwise, do nothing if (amt 0) { balance = balance + amt; } } //Deducts the passed amount from the balance public void deduct(double amt) { //deduct if amt is positive; do nothing otherwise if (amt 0) { double newbalance = balance - amt; if (newbalance = 0) { //if a new balance is positive, then balance = newbalance; //update the balance; otherwise, } //do nothing. } } ... } add deduct 1. Rewrite the following nested if statements without using any nesting. a. if ( a c ) if ( b c ) x = y; else x = z; wu23399_ch05.qxd 12/14/06 17:49 Page 230
  • 258. else x = z; b. if ( a == b ) x = y; else if ( a b ) x = y; else x = z; c. if ( a b ) if ( a = b ) x = z; else x = y; else x = z; 2. Format the following if statements with indentation. a. if ( a b ) if ( c d ) x = y; else x = z; b. if ( a b ) { if ( c d ) x = y; } else x = z; c. if ( a b ) x = y; if ( a c ) x = z; else if ( c d ) z = y; 5.3 Boolean Expressions and Variables In addition to the arithmetic operators introduced in Chapter 3 and relational oper- ators introduced in Section 5.2, boolean expressions can contain conditional and boolean operators. A boolean operator, also called a logical operator, takes boolean values as its operands and returns a boolean value. Three boolean operators are AND, OR, and NOT. In Java, the symbols , ||, and ! represent the AND, OR, and NOT operators, respectively. Table 5.1 explains how these operators work. The AND operation results in true only if both P and Q are true. The OR op- eration results in true if either P or Q is true. The NOT operation is true if A is false and is false if P is true. Combining boolean operators with relational and arithmetic operators, we can come up with long boolean expressions such as (x + 150) == y || x y !(y z z x) (x y) (a == b || a == c) a != 0 b != 0 (a + b 10) In Section 5.1 we stated that we can reverse the relational operator and switch the then and else blocks to derive the equivalent code. For example, if (age 0) { System.out.println(Invalid age is entered); } else { System.out.println(Valid age is entered); } 5.3 Boolean Expressions and Variables 231 boolean operator wu23399_ch05.qxd 12/14/06 17:49 Page 231
  • 259. is equivalent to if ( !(age 0) ) { System.out.println(Valid age is entered); } else { System.out.println(Invalid age is entered); } which can be written more naturally as if (age = 0) { System.out.println(Valid age is entered); } else { System.out.println(Invalid age is entered); } Reversing the relational operator means negating the boolean expression. In other words, !(age 0) is equivalent to (age = 0). Now, consider the following if-then-else statement: if (temperature = 65 distanceToDestination 2) { System.out.println(Let's walk); } else { System.out.println(Let's drive); } If the temperature is greater than or equal to 65 degrees and the distance to the destination is less than 2 mi., we walk. Otherwise (it’s too cold or too far away), we drive. How do we reverse the if-then-else statement? We can rewrite the statement by negating the boolean expression and switching the then and else blocks as if ( !(temperature = 65 distanceToDestination 2) ) { System.out.println(Let's drive); } else { System.out.println(Let's walk); } 232 Chapter 5 Selection Statements Table Table 5.1 Boolean operators and their meanings P Q P Q P | | Q !P false false false false true false true false true true true false false true false true true true true false wu23399_ch05.qxd 12/14/06 17:49 Page 232
  • 260. or more directly and naturally as if (temperature 65 || distanceToDestination = 2) { System.out.println(Let's drive); } else { System.out.println(Let's walk); } The expression !(temperature = 65 distanceToDestination 2) is equivalent to !(temperature = 65) || !(distanceToDestination 2) which, in turn, is equivalent to (temperature 65 || distanceToDestination = 2) The logical equivalence is derived by applying the following DeMorgan’s law: Table 5.2 shows their equivalence. Now consider the following expression: x / y z || y == 0 What will be the result if y is equal to 0? Easy, the result is true, many of you might say. Actually a runtime error called an arithmetic exception will result, because the expression x / y Equivalence symbol Rule 1: !(P Q) !P || !Q Rule 2: !(P || Q) !P !Q 5.3 Boolean Expressions and Variables 233 arithmetic exception Table Table 5.2 The truth table illustrating DeMorgan’s law P Q !(P Q) !P | | !Q !(P | | Q) !P !Q false false true true true true false true true true false false true false true true false false true true false false false false wu23399_ch05.qxd 12/14/06 17:49 Page 233
  • 261. causes a problem known as a divide-by-zero error. Remember that you cannot divide a number by zero. However, if we reverse the order to y == 0 || x / y z then no arithmetic exception will occur because the test x / y z will not be evalu- ated. For the OR operator ||, if the left operand is evaluated to true, then the right operand will not be evaluated, because the whole expression is true, whether the value of the right operand is true or false. We call such an evaluation method a short-circuit evaluation. For the AND operator , the right operand need not be evaluated if the left operand is evaluated to false, because the result will then be false whether the value of the right operand is true or false. Just as the operator precedence rules are necessary to evaluate arithmetic expressions unambiguously, they are required for evaluating boolean expressions. Table 5.3 expands Table 3.3 by including all operators introduced so far. In mathematics, we specify the range of values for a variable as 80 x 90 In Java, to test that the value for x is within the specified lower and upper bounds, we express it as 80 = x x 90 You cannot specify it as 80 = x 90 This is a syntax error because the relational operators (, =, etc.) are binary operators whose operands must be numerical values. Notice that the result of the subexpression 80 = x is a boolean value, which cannot be compared to the numerical value 90. Their data types are not compatible. The result of a boolean expression is either true or false, which are the two values of data type boolean. As is the case with other data types, a value of a data type can be assigned to a variable of the same data type. In other words, we can declare a variable of data type boolean and assign a boolean value to it. Here are examples: boolean pass, done; pass = 70 x; done = true; One possible use of boolean variables is to keep track of the program settings or user preferences. A variable (of any data type, not just boolean) used for this 234 Chapter 5 Selection Statements divide-by-zero error short-circuit evaluation Wrong wu23399_ch05.qxd 12/14/06 17:49 Page 234
  • 262. purpose is called a flag. Suppose we want to allow the user to display either short or long messages. Many people, when using a new program, prefer to see long messages, such as Enter a person’s age and press the Enter key to continue. But once they are familiar with the program, many users prefer to see short messages, such as Enter age. We can use a boolean flag to remember the user’s preference. We can set the flag longMessageFormat at the beginning of the program to true or false depending on the user’s choice. Once this boolean flag is set, we can refer to the flag at different points in the program as follows: if (longMessageFormat) { //display the message in long format 5.3 Boolean Expressions and Variables 235 flag Table Table 5.3 Operator precedence rules.Groups are listed in descending order of prece- dence.An operator with a higher precedence will be evaluated first.If two operators have the same precedence,then the associativity rule is applied Group Operator Precedence Associativity Subexpression ( ) 10 Left to right (If parentheses are nested, then innermost subexpres- sion is evaluated first.) Postfix ++ 9 Right to left increment and -- decrement operators Unary - 8 Right to left operators ! Multiplicative * 7 Left to right operators / % Additive + 6 Left to right operators - Relational 5 Left to right operators = = Equality == 4 Left to right operators != Boolean AND 3 Left to right Boolean OR || 2 Left to right Assignment = 1 Right to left wu23399_ch05.qxd 12/14/06 17:49 Page 235
  • 263. } else { //display the message in short format } Notice the value of a boolean variable is true or false, so even though it is valid, we do not write a boolean expression as if (isRaining == true) { System.out.println(Store is open); } else { System.out.println(Store is closed); } but more succinctly as if (isRaining) { System.out.println(Store is open); } else { System.out.println(Store is closed); } Another point that we have to be careful about in using boolean variables is the choice of identifier. Instead of using a boolean variable such as motionStatus, it is more meaningful and descriptive to use the variable isMoving. For example, the statement if (isMoving) { //the mobile robot is moving } else { //the mobile robot is not moving } is much clearer than the statement if (motionStatus) { //the mobile robot is moving } else { //the mobile robot is not moving } When we define a boolean data member for a class, it is a Java convention to use the prefix is instead of get for the accessor. We again conclude the section with a sample class. Let’s improve the Ch5Account class by adding a boolean data member active to represent the state of an account. When an account is first open, it is set to an active state. Deposits and 236 Chapter 5 Selection Statements wu23399_ch05.qxd 12/14/06 17:49 Page 236
  • 264. withdrawals can be made only when the account is active. If the account is inactive, then the requested opertion is ignored. Here’s how the class is defined (the actual class name is Ch5AccountVer2): 5.3 Boolean Expressions and Variables 237 class Ch5AccountVer2 { // Data Members private String ownerName; private double balance; private boolean active; //Constructor public Ch5AccountVer2(String name, double startingBalance ) { ownerName = name; balance = startingBalance; setActive(true); } //Adds the passed amount to the balance public void add(double amt) { //add if amt is positive; do nothing otherwise if (isActive() amt 0) { balance = balance + amt; } } //Closes the account; set 'active' to false public void close( ) { setActive(false); } //Deducts the passed amount from the balance public void deduct(double amt) { //deduct if amt is positive; do nothing otherwise if (isActive() amt 0) { double newbalance = balance - amt; if (newbalance = 0) { //don't let the balance become negative balance = newbalance; } } } Data Members add close deduct wu23399_ch05.qxd 12/14/06 17:49 Page 237
  • 265. //Returns the current balance of this account public double getCurrentBalance( ) { return balance; } //Returns the name of this account's owner public String getOwnerName( ) { return ownerName; } //Is the account active? public boolean isActive( ) { return active; } //Assigns the name of this account's owner public void setOwnerName(String name) { ownerName = name; } //Sets 'active' to true or false private void setActive(boolean state) { active = state; } } 238 Chapter 5 Selection Statements getCurrentBalance getOwnerName setOwnerName isActive setActive 1. Evaluate the following boolean expressions. Assume x, y, and z have some numerical values. a. 4 5 || 6 == 6 b. 2 4 (false || 5 = 4) c. x = y !(z != z) || x y d. x y || z y y = z 2. Identify errors in the following boolean expressions and assignments. Assume x and y have some numerical values. a. boolean done; done = x = y; b. 2 4 (3 5) + 1 == 3 c. boolean quit; quit = true; quit == ( 34 == 20 ) quit; wu23399_ch05.qxd 12/14/06 17:49 Page 238
  • 266. 5.4 Comparing Objects With primitive data types, we have only one way to compare them, but with objects (reference data type), we have two ways to compare them. We discuss the ways the objects can be compared in this section. First, let’s review how we compare primitive data types. What would be the output of the following code? int num1, num2; num1 = 15; num2 = 15; if (num1 == num2) { System.out.println(They are equal); } else { System.out.println(They are not equal); } Because the two variables hold the same value, the output is They are equal Now, let’s see how the objects can be compared. We will use String objects for illustration. Since we use string data all the time in our programs, it is very impor- tant for us to understand perfectly how String objects can be compared. Consider the following code that attempts to compare two String objects: String str1, str2; str1 = new String(Java); str2 = new String(Java); if (str1 == str2) { System.out.println(They are equal); 5.4 Comparing Objects 239 We introduced the logical AND and OR operations using the symbols and ||. In Java, there are single ampersand and single vertical bar operations. For exam- ple,if we write an if statement as if ( 70 = x x 90 ) it will compile and run. Unlike the double ampersand, the single ampersand will not do a short-circuit evaluation. It will evaluate both left and right operands.The single vertical bar works in an analogous manner. So, which one should we use? Use double ampersand for AND and double vertical bars for OR.We will most likely never encounter a situation where we cannot use the double ampersand or the double vertical bars. wu23399_ch05.qxd 12/14/06 17:49 Page 239
  • 267. } else { System.out.println(They are not equal); } What would be an output? The answer is They are not equal The two objects are constructed with the same sequence of characters, but the result of comparison came back that they were not equal. Why? When two variables are compared, we are comparing their contents. In the case of primitive data types, the content is the actual value. In case of reference data types, the content is the address where the object is stored. Since there are two dis- tinct String objects, stored at different addresses, the contents of str1 and str2 are different, and therefore, the equality testing results in false. If we change the code to String str1, str2; str1 = new String(Java); str2 = str1; if (str1 == str2) { System.out.println(They are equal); } else { System.out.println(They are not equal); } then the output would be They are equal because now we have one String object and both variables str1 and str2 point to this object. This means the contents of str1 and str2 are the same because they refer to the same address. Figure 5.5 shows the distinction. What can we do if we need to check whether two distinct String objects have the same sequence of characters? Many standard classes include different types of comparison methods. The String class, for example, includes the equals and equalsIgnoreCase comparison methods. The equals method returns true if two String objects have the exact same sequence of characters. The equalsIgnoreCase method does the same as the equals method, but the comparison is done in a case-insensitive manner. Using the equals method, we can rewrite the first sample code as String str1, str2; str1 = new String(Java); str2 = new String(Java); if (str1.equals(str2)) { System.out.println(They are equal); } else { System.out.println(They are not equal); } 240 Chapter 5 Selection Statements No new object is created here.The content (address) of str1 is copied to str2,making them both point to the same object. Use the equals method. wu23399_ch05.qxd 12/14/06 17:49 Page 240
  • 268. and get the result They are equal Just as the String and many standard classes provide the equals method, it is common to include such an equals method in programmer-defined classes also. Consider a Fraction class. We say two Fraction objects are equal if they have the same value for the numerator and the denominator. Here’s how we can define the equals method for the Fraction class: class Fraction { private int numerator; private int denominator; ... //constructor and other methods ... public int getNumerator( ) { return numerator; } 5.4 Comparing Objects 241 :String Java String str1, str2; str1 = new String(Java); str2 = new String(Java); str1 Case A: Two variables refer to two different objects. :String Java :String Java str1 str2 false String str1, str2; str1 = new String(Java); str2 = str1; str1 str2 true str2 str1 Case B: Two variables refer to the same object. str2 Figure 5.5 How the equality == testing works with the objects. wu23399_ch05.qxd 12/14/06 17:49 Page 241
  • 269. public int getDenominator( ) { return denominator; } public boolean equals(Fraction number) { return (numerator == number.getNumerator() denominator == number.getDenominator()); } } Notice that the body of the equals method is a concise version of if (numerator == number.getNumerator( ) denominator == number.getDenominator()) { return true; } else { return false; } Using the equals method, we can compare two Fraction objects in the follow- ing manner: Fraction frac1, frac2; //create frac1 and frac2 objects ... if (frac1.equals(frac2)) { ... } or equivalently as if (frac2.equals(frac1)) { ... } Note that the equals method as defined is incomplete. For example, if we compare fractions 48 and 36, using this equals method, we get false as the result because the method does not compare the fractions in their simplified form. The method should have reduced both 48 and 36 to 12 and then compared. To implement a method that reduces a fraction to its simplest form, we need to use a repetition control statement. We will revisit this problem when we learn how to write repetition control statements in Chapter 6. Also, we will provide the complete definition of the Fraction class in Chapter 7. We conclude this section by presenting an exception to the rule for comparing objects. This exception applies to the String class only. We already mentioned in 242 Chapter 5 Selection Statements Compare this object’s values to the values of number wu23399_ch05.qxd 12/14/06 17:49 Page 242
  • 270. Chapter 2 that for the String class only, we do not have to use the new operator to create an instance. In other words, instead of writing String str = new String(Java); we can write String str = Java; which is a more common form. These two statements are not identical in terms of memory allocation, which in turn affects how the string comparisons work. Figure 5.6 shows the difference in assigning a String object to a variable. If we do not use the new operator, then string data are treated as if they are a primitive data type. When we use the same literal String constants in a program, there will be exactly one String object. This means we can use the equal symbol == to compare String objects when no new operators are used. However, regardless of how the String objects are cre- ated, it is always correct and safe to use the equals and other comparison methods to compare two strings. 5.4 Comparing Objects 243 :String Java String word1, word2; word1 = new String(Java); word2 = new String(Java); word1 :String Java :String Java word1 word2 false word2 word1 word2 Whenever the new operator is used, there will be a new object. String word1, word2; word1 = Java; word2 = Java; word1 word2 true Literal string constant such as “Java” will always refer to one object. Figure 5.6 Difference between using and not using the new operator for String. Always use the equals and other comparison methods to compare String objects. Do not use == even though it may work correctly in certain cases. wu23399_ch05.qxd 12/14/06 17:49 Page 243
  • 271. 244 Chapter 5 Selection Statements 1. Determine the output of the following code. String str1 = Java; String str2 = Java; boolean result1 = str1 == str2; boolean result2 = str1.equals(str2); System.out.println(result1); System.out.println(result2); 2. Determine the output of the following code. String str1 = new String(latte); String str2 = new String(LATTE); boolean result1 = str1 == str2; boolean result2 = str1.equals(str2); System.out.println(result1); System.out.println(result2); 3. Show the state of memory after the following statements are executed. String str1, str2, str3; str1 = Jasmine; str2 = Oolong; str3 = str2; str2 = str1; 5.5 The switch Statement Another Java statement that implements a selection control flow is the switch statement. Suppose we want to direct the students to the designated location for them to register for classes. The location where they register is determined by their grade level. The user enters 1 for freshman, 2 for sophomore, 3 for junior, and 4 for senior. Using the switch statement, we can write the code as int gradeLevel; Scanner scanner = new Scanner(System.in); System.out.print(Grade (Frosh-1,Soph-2,...): ); gradeLevel = scanner.nextInt(); switch (gradeLevel) { case 1: System.out.println(Go to the Gymnasium); break; case 2: System.out.println(Go to the Science Auditorium); break; switch statement wu23399_ch05.qxd 12/14/06 17:49 Page 244
  • 272. case 3: System.out.println(Go to Halligan Hall Rm 104); break; case 4: System.out.println(Go to Root Hall Rm 101); break; } The syntax for the switch statement is switch ( integer expression ) { case label 1 : case body 1 ... case label n : case body n } Figure 5.7 illustrates the correspondence between the switch statement we wrote and the general format. The case label i has the form case integer constant or default and case body i is a sequence of zero or more statements. Notice that case body i is not surrounded by left and right braces. The constant can be either a named or literal constant. The data type of arithmetic expression must be char, byte, short, or int. (Note: We will cover the data type char in Chap. 9.) The value of arithmetic expression is compared against the constant value i of case label i. If there is a 5.5 The switch Statement 245 switch statement syntax default reserved word Figure 5.7 Mapping of the sample switch statement to the general format. gradeLevel ) { switch ( } case 1: case 2: System.out.println(Go to the Gymnasium); break; System.out.println(Go to the Science Auditorium); break; case 3: System.out.println(Go to Halligan Hall Rm 104); break; case 4: System.out.println(Go to Root Hall Rm 101); break; Integer Expression CL1 CB1 CL2 CB2 CL3 CB3 CL4 CB4 CB - case body CL - case label wu23399_ch05.qxd 12/14/06 17:49 Page 245
  • 273. matching case, then its case body is executed. If there is no matching case, then the execution continues to the statement that follows the switch statement. No two cases are allowed to have the same value for constant, and the cases can be listed in any order. Notice that each case in the sample switch statement is terminated with the break statement. The break statement causes execution to continue from the state- ment following this switch statement, skipping the remaining portion of the switch statement. The following example illustrates how the break statement works: //Assume necessary declaration and object creation are done selection = 1; switch (selection) { case 0: System.out.println(0); case 1: System.out.println(1); case 2: System.out.println(2); case 3: System.out.println(3); } When this code is executed, the output is 1 2 3 because after the statement in case 1 is executed, statements in the remaining cases will be executed also. To execute statements in one and only one case, we need to include the break statement at the end of each case, as we have done in the first example. Figure 5.8 shows the effect of the break statement. The break statement is not necessary in the last case, but for consistency we place it in every case. Also, by doing so we don’t have to remember to include the break statement in the last case when we add more cases to the end of the switch statement. Individual cases do not have to include a statement, so we can write some- thing like this: Scanner scanner = new Scanner(System.in); System.out.print(Input: ); int ranking = scanner.nextInt(); switch (ranking) { case 10: case 9: case 8: System.out.println(Master); break; case 7: case 6: System.out.println(Journeyman); break; 246 Chapter 5 Selection Statements break statement wu23399_ch05.qxd 12/14/06 17:49 Page 246
  • 274. case 5: case 4: System.out.println(Apprentice); break; } The code will print Master if the value of ranking is 10, 9, or 8; Journeyman if the value of ranking is either 7 or 6; or Apprentice if the value of ranking is either 5 or 4. We may include a default case that will always be executed if there is no matching case. For example, we can add a default case to print out an error message if any invalid value for ranking is entered. switch (ranking) { case 10: case 9: case 8: System.out.println(Master); break; case 7: case 6: System.out.println(Journeyman); break; case 5: 5.5 The switch Statement 247 Figure 5.8 A diagram showing the control flow of the switch statement with and without the break statements. false false false true true true true true true switch ( N ) { case 1: x = 10; break; case 2: x = 20; break; case 3: x = 30; break; } switch ( N ) { case 1: x = 10; case 2: x = 20; case 3: x = 30; } x = 10; N == 1? x = 20; N == 2? x = 30; N == 3? false false false N == 1? N == 2? N == 3? x = 10; break; break; break; x = 20; x = 30; wu23399_ch05.qxd 12/14/06 17:49 Page 247
  • 275. case 4: System.out.println(Apprentice); break; default: System.out.println(Error: Invalid Data); break; } There can be at most one default case. Since the execution continues to the next statement if there is no matching case (and no default case is specified), it is safer to always include a default case. By placing some kind of output statement in the default case, we can detect an unexpected switch value. Such a style of program- ming is characterized as defensive programming. Although the default case does not have to be placed as the last case, we recommend you do so, in order to make the switch statement more readable. 248 Chapter 5 Selection Statements defensive programming 1. What’s wrong with the following switch statement? switch ( N ) { case 0: case 1: x = 11; break; default: System.out.println(Switch Error); break; case 2: x = 22; break; case 1: x = 33; break; } 2. What’s wrong with the following switch statement? switch ( ranking ) { case 4.55: pay = pay * 0.20; break; case =4.55: pay = pay * 0.15; break; default: pay = pay * 0.05; break; } 5.6 Drawing Graphics We introduce four standard classes related to drawing geometric shapes on a window. These four standard classes will be used in Section 5.7 on the sample development. We describe their core features here. More details can be found in the online Java API documentation. wu23399_ch05.qxd 12/14/06 17:49 Page 248
  • 276. java.awt.Graphics We can draw geometric shapes on a frame window by calling appropriate methods of the Graphics object. For example, if g is a Graphics object, then we can write g.drawRect(50, 50, 100, 30); to display a rectangle 100 pixels wide and 30 pixels high at the specified position (50, 50). The position is determined as illustrated in Figure 5.9. The complete pro- gram is shown below. The top left corner, just below the window title bar, is posi- tion (0, 0), and the x value increases from left to right and the y value increases from top to bottom. Notice that the direction in which the y value increases is opposite to the normal two-dimensional graph. The area of a frame which we can draw is called the content pane of a frame. The content pane excludes the area of a frame that excludes the regions such as the border, scroll bars, the title bar, the menu bar, and others. To draw on the content pane of a frame window, first we must get the content pane’s Graphic object. Then we call this Graphics method to draw geometric shapes. Here’s a sample: 5.6 Drawing Graphics 249 java.awt. Graphics content pane of a frame /* Chapter 5 Sample Program: Draw a rectangle on a frame window's content pane File: Ch5SampleGraphics.java */ import javax.swing.*; //for JFrame import java.awt.*; //for Graphics and Container class Ch5SampleGraphics { public static void main( String[] args ) { JFrame win; Container contentPane; Graphics g; win = new JFrame(My First Rectangle); win.setSize(300, 200); win.setLocation(100,100); win.setVisible(true); contentPane = win.getContentPane(); g = contentPane.getGraphics(); g.drawRect(50,50,100,30); } } win must be visible on the screen before you get its content pane. wu23399_ch05.qxd 12/14/06 17:49 Page 249
  • 277. Here are the key points to remember in drawing geometric shapes on the con- tent pane of a frame window. 250 Chapter 5 Selection Statements To draw geometric shapes on the content pane of a frame window: 1. The content pane is declared as a Container, for example, Container contentPane; 2. The frame window must be visible on the screen before we can get its content pane and the content pane’s Graphics object. Depending on the speed of your PC, you may have to include the following try statement try {Thread.sleep(200);} catch (Exception e) {} to put a delay before drawing the rectangle. Place this try statement before the last statement.The argument in the sleep method specifies the amount of delay in milliseconds (1000 ms = 1 s). If you still do not see a rectangle drawn in the window after including the delay statement, increase the amount of delay until you see the rectangle drawn.We will describe the try statement in Chapter 8. graphic.drawRect( x, y, width, height); graphic.drawRect(50, 50, 100, 30); Syntax A rectangle width wide and height high is displayed at position (x, y). Position (50, 50) Position (0, 0) 30 100 x y Example: Figure 5.9 The diagram illustrates how the position of the rectangle is determined by the drawRect method. wu23399_ch05.qxd 12/14/06 17:49 Page 250
  • 278. Table 5.4 lists some of the available graphic drawing methods. 5.6 Drawing Graphics 251 Table Table 5.4 A partial list of drawing methods defined for the Graphics class Method Meaning drawLine(x1,y1,x2,y2) Draws a line between (x1,y1) and (x2,y2). drawRect(x,y,w,h) Draws a rectangle with width w and height h at (x,y). drawRoundRect(x,y,w,h,aw,ah) Draws a rounded-corner rectangle with width w and height h at (x,y). Parameters aw and ah determine the angle for the rounded corners. (x,y) h w aw ah (x,y) h w (x1,y1) (x2,y2) If there is a window that covers the area in which the drawing takes place or the draw- ing window is minimized and restored to its normal size, the drawn shape (or portion of it, in the case of the overlapping windows) gets erased. The DrawingBoard class used in the sample development (Sec. 5.7) eliminates this problem. For information on the technique to avoid the disappearance of the drawn shape, please check our website at www.drcaffeine.com wu23399_ch05.qxd 12/14/06 17:49 Page 251
  • 279. Notice the distinction between the draw and fill methods. The draw method will draw the boundary only, while the fill method fills the designated area with the currently selected color. Figure 5.10 illustrates the difference. java.awt.Color To designate the color for drawing, we will use the Color class from the standard java.awt package. A Color object uses a coloring scheme called the RGB scheme, which specifies a color by combining three values, ranging from 0 to 255, for red, green, and blue. For example, the color black is expressed by setting red, green, and blue to 0, and the color white by setting all three values to 255. We create, for example, a Color object for the pink color by executing Color pinkColor; pinkColor = new Color(255,175,175); 252 Chapter 5 Selection Statements Table Table 5.4 A partial list of drawing methods defined for the Graphics class (Continued) Method Meaning drawOval(x,y,w,h) Draws an oval with width w and height h at (x,y). drawString(text,x,y) Draws the string text at (x,y). fillRect(x,y,w,h) Same as the drawRect method but fills the region with the currently set color. fillRoundRect(x,y,w,h,aw,ah) Same as the drawRoundRect method but fills the region with the currently set color. fillOval(x,y,w,h) Same as the drawOval method but fills the region with the currently set color. (x,y) text (x,y) h w java.awt.Color wu23399_ch05.qxd 12/14/06 17:49 Page 252
  • 280. Instead of dealing with the three numerical values, we can use the public class constants defined in the Color class. The class constants for common colors are Color.BLACK Color.MAGENTA Color.BLUE Color.ORANGE Color.CYAN Color.PINK Color.DARK_GRAY Color.RED Color.GRAY Color.WHITE Color.GREEN Color.YELLOW Color.LIGHT_GRAY The class constants in lowercase letters are also defined (such as Color.black, Color.blue, and so forth). In the older versions of Java, only the constants in lowercase letters were defined. But the Java convention is to name constants using only the uppercase letters, so the uppercase color constants are added to the class definition. Each of the above is a Color object with its RGB values correctly set up. We will pass a Color object as an argument to the setColor method of the Graphics class to change the color. To draw a blue rectangle, for example, we write //Assume g is set correctly g.setColor(Color.BLUE); g.drawRect(50, 50, 100, 30); We can also change the background color of a content pane by using the setBackground method of Container as contentPane.setBackground(Color.LIGHT_GRAY); 5.6 Drawing Graphics 253 g.drawRect(50, 50, 100, 30); g.fillRect(175, 50, 100, 30); Figure 5.10 The diagram illustrates the distinction between the draw and fill methods.We assume the currently selected color is black (default). wu23399_ch05.qxd 12/14/06 17:49 Page 253
  • 281. Running the following program will result in the frame shown in Figure 5.11. 254 Chapter 5 Selection Statements Figure 5.11 A frame with a white background content pane and two rectangles. /* Chapter 5 Sample Program: Draw one blue rectangle and one filled red rectangle on light gray background content pane File: Ch5SampleGraphics2.java */ import javax.swing.*; import java.awt.*; class Ch5SampleGraphics2 { public static void main( String[] args ) { JFrame win; Container contentPane; Graphics g; win = new JFrame(Rectangles); win.setSize(300, 200); win.setLocation(100,100); win.setVisible(true); contentPane = win.getContentPane(); contentPane.setBackground(Color.LIGHT_GRAY); g = contentPane.getGraphics(); g.setColor(Color.BLUE); g.drawRect(50,50,100,30); wu23399_ch05.qxd 12/14/06 17:49 Page 254
  • 282. g.setColor(Color.RED); g.fillRect(175,50,100,30); } } 5.6 Drawing Graphics 255 java.awt.Point A Point object is used to represent a point in two-dimensional space. It contains x and y values, and we can access these values via its public data member x and y. Here’s an example to assign a position (10, 20): Point pt = new Point(); pt.x = 10; pt.y = 20; It is also possible to set the position at the creation time as follows: Point pt = new Point(10, 20); java.awt.Dimension In manipulating shapes, such as moving them around a frame’s content pane, the concept of the bounding rectangle becomes important. A bounding rectangle is a rectangle that completely surrounds the shape. Figure 5.12 shows some examples of bounding rectangles. Just as the (x, y) values are stored as a single Point object, we can store the width and height of a bounding rectangle as a single Dimension object. The Dimension class has the two public data members width and height to maintain the width and height of a bounding rectangle. Here’s an example to create a 40 pixels by 70 pixels high bounding rectangle: Dimension dim = new Dimension(); dim.width = 40; dim.height = 70; Bounding rectangle of a rectangle is the rectangle itself. Bounding rectangle Figure 5.12 Bounding rectangles of various shapes. wu23399_ch05.qxd 12/14/06 17:49 Page 255
  • 283. It is also possible to set the values at the creation time as follows: Dimension dim = new Dimension(40, 70); Let’s apply the drawing techniques to an early sample program. In Chapter 4, we wrote the RoomWinner program that randomly selects and displays the dorm room lottery cards. The display was only in text, something like this: Winning Card Combination: 1 - red; 2 - green; 3 - blue color number Card 1: 2 13 Card 2: 2 12 Card 3: 1 14 We will make a graphical version of the program. Figure 5.13 shows a sample output. Here’s the main class Ch5RoomWinner, which has a structure similar to the one for Ch5SampleGraphics2. 256 Chapter 5 Selection Statements Figure 5.13 Sample output of Ch5RoomWinner program. import java.awt.*; import javax.swing.*; class Ch5RoomWinner { public static void main( String[] args ) { JFrame win; Container contentPane; Graphics g; wu23399_ch05.qxd 12/14/06 17:49 Page 256
  • 284. GraphicLotteryCard one, two, three; win = new JFrame(Room Winner); win.setSize(300, 200); win.setLocation(100,100); win.setVisible(true); contentPane = win.getContentPane(); contentPane.setBackground(Color.WHITE); g = contentPane.getGraphics(); one = new GraphicLotteryCard( ); two = new GraphicLotteryCard( ); three = new GraphicLotteryCard( ); one.spin(); two.spin(); three.spin(); one.draw(g, 10, 20); two.draw(g, 50, 20); three.draw(g, 90, 20); } } 5.6 Drawing Graphics 257 These objects will draw themselves on g at the specified positions. We modify the LotteryCard class from Chapter 4 by adding code that will draw a card on a given Graphics context. The name of the new class is GraphicLotteryCard. Here’s the class definition (we list only the portions that are new): import java.awt.*; class GraphicLotteryCard { // Data Members //width of this card for drawing public static final int WIDTH = 30; //height of this card for drawing public static final int HEIGHT = 40; //the other data members and methods are the same as before public void draw(Graphics g, int xOrigin, int yOrigin) { switch (color) { case 1: g.setColor(Color.RED); break; wu23399_ch05.qxd 12/14/06 17:49 Page 257
  • 285. case 2: g.setColor(Color.GREEN); break; case 3: g.setColor(Color.BLUE); break; } g.fillRect(xOrigin, yOrigin, WIDTH, HEIGHT); g.setColor(Color.WHITE); //draw text in white g.drawString( + number, xOrigin + WIDTH/4, yOrigin + HEIGHT/2); } } 258 Chapter 5 Selection Statements This is a quick way to convert a numerical value to String Notice that the statements in Ch5RoomWinner one.draw(g, 10, 20); two.draw(g, 50, 20); three.draw(g, 90, 20); are not as flexible as they can be. If the values for the constant WIDTH and HEIGHT in the GraphicLotteryCard class are changed, these three statements could result in drawing the card inadequately (such as overlapping cards). The two constants are declared public for a reason. Using the WIDTH constant, for example, we can rewrite the three statements as int cardWidth = GraphicLotteryCard.WIDTH; one.draw(g, 10, 20); two.draw(g, 10 + cardWidth + 5, 20); three.draw(g, 10 + 2*(cardWidth+ 5), 20); The statements will draw cards with a 5-pixel interval between cards. This code will continue to work correctly even after the value of WIDTH is modified. 5.7 Enumerated Constants We learned in Section 3.3 how to define numerical constants and the benefits of using them in writing readable programs. In this section, we will introduce an additional type of constant called enumerated constants that were added to the Java language from Version 5.0. Let’s start with an example. Suppose we want to define a Student class and define constants to distinguish four undergraduate grade enumerated constants wu23399_ch05.qxd 12/14/06 17:49 Page 258
  • 286. levels—freshman, sophomore, junior, and senior. Using the numerical constants, we can define the grade levels as such: class Student { public static final int FRESHMAN = 0; public static final int SOPHOMORE = 1; public static final int JUNIOR = 2; public static final int SENIOR = 3; ... } With the new enumerated constants, this is how we can define the grade lev- els in the Student class: class Student { public static enum GradeLevel {FRESHMAN, SOPHOMORE, JUNIOR, SENIOR} ... } The word enum is a new reserved word, and the basic syntax for defining enumer- ated constants is enum enumerated type { constant values } where enumerated type is an identifier and constant values is a list of identi- fiers separated by commas. Notice that for the most common usage of enumerated constants, we append the modifiers public and static; but they are not a required part of defining enumerated constants. Here are more examples: enum Month {JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER} enum Gender {MALE, FEMALE} enum SkillLevel {NOVICE, INTERMEDIATE, ADVANCED, EXPERT} One restriction when declaring an enumerated type is that it cannot be a local declaration. In other words, we must declare it outside of any method, just as for the other data members of a class. Unlike numerical constants, which are simply identifiers with fixed numerical values, enumerated constants do not have any assigned numerical values. They are said to belong to, or be members of, the associated enumerated type. For example, two enumerated constants MALE and FEMALE belong to the enumerated type Gender. (Note: We keep the discussion of the enumerated type to its simplest form here. It is beyond the scope of an introductory programming textbook to discuss Java’s enumerated type in full detail.) 5.7 Enumerated Constants 259 enumerated type wu23399_ch05.qxd 12/14/06 17:49 Page 259
  • 287. Just as with any other data types, we can declare variables of an enumerated type and assign values to them. Here is an example (for the sake of brevity, we list the enum declaration and its usage together, but remember that the declaration in the actual use cannot be a local declaration): enum Fruit {APPLE, ORANGE, BANANA} Fruit f1, f2, f3; f1 = Fruit.APPLE; f2 = Fruit.BANANA; f3 = f1; Because variables f1, f2, and f3 are declared to be of the type Fruit, we can only assign one of the associated enumerated constants to them. This restriction supports a desired feature called type safety. So what is the big deal? Consider the following numerical constants and assignment statements: final int APPLE = 1; final int ORANGE = 2; final int BANANA = 3; int fOne, fTwo, fThree; fOne = APPLE; fTwo = ORANGE; fThree = fOne; The code may look comparable to the one that uses enumerated constants, but what will happen if we write the following? fOne = 45; The assignment is logically wrong. It does not make any sense to assign meaning- less value such as 45 to the variable fOne if it is supposed to represent one of the de- fined fruit. However, no compiler error will result because the data type of fOne is int. The statement may or may not cause the runtime error depending on how the variable fOne is used in the rest of the program. In either case, the program cannot be expected to produce a correct result because of the logical error. By defining an enumerated type, a variable of that type can only accept the associated enumerated constants. Any violation will be detected by the compiler. This will eliminate the possibility of assigning a nonsensical value as seen in the case for the numerical constants. Type safety means that we can assign only meaningful values to a declared variable. Another benefit of the enumerated type is the informative output values. As- suming the variables fTwo and f2 retain the values assigned to them in the sample code, the statement System.out.println(Favorite fruit is + fTwo); 260 Chapter 5 Selection Statements NOTE: The constant value is prefixed by the name of the enumerated type. type safety wu23399_ch05.qxd 12/14/06 17:49 Page 260
  • 288. will produce a cryptic output: Favorite fruit is 2 In contrast, the statement System.out.println(Favorite fruit is + f2); will produce a more informative output: Favorite fruit is BANANA We will describe other advantages of using enumerated types later in the book. As shown, when referring to an enumerated constant in the code, we must pre- fix it with its enumerated type name, for example, Fruit f = Fruit.APPLE; . . . if (f == Fruit.ORANGE) { System.out.println(I like orange, too.); } . . . A case label for a switch statement is the only exception to this rule. Instead of writing, for example, Fruit fruit; fruit = ... ; switch (fruit) { case Fruit.APPLE: . . .; break; case Fruit.ORANGE: . . .; break; case Fruit.BANANA: . . .; break; } we can specify the case labels without the prefix as in Fruit fruit; fruit = ...; switch (fruit) { case APPLE: . . .; break; 5.7 Enumerated Constants 261 wu23399_ch05.qxd 12/14/06 17:49 Page 261
  • 289. case ORANGE: . . .; break; case BANANA: . . .; break; } 262 Chapter 5 Selection Statements It is not necessary to prefix the enumerated constant with its enumerated type name when it is used as a case label in a switch statement. The enumerated type supports a useful method named valueOf. The method accepts one String argument and returns the enumerated constant whose value matches the given argument. For example, the following statement assigns the enu- merated constant APPLE to the variable fruit: Fruit fruit = Fruit.valueOf(APPLE); In which situations could the valueOf method be useful? One is the input rou- tine. Consider the following: Scanner scanner = new Scanner(System.in); System.out.print(Enter your favorite fruit + (APPLE, ORANGE, BANANA): ); String fruitName = scanner.next( ); Fruit favoriteFruit = Fruit.valueOf(fruitName); Be aware, however, that if you pass a String value that does not match any of the de- fined constants, it will result in a runtime error. This means if the user enters Orange, for example, it will result in an error (the input has to be all capital letters to match). We will discuss how to handle such runtime errors without causing the program to terminate abruptly in Chapter 8. To access the enumerated constants in a programmer-defined class from outside the class, we must reference them through the associated enumerated type (assuming, of course, the visibility modifier is public). Consider the following Faculty class: class Faculty { public static enum Rank {LECTURER, ASSISTANT, ASSOCIATE, FULL} private Rank rank; . . . wu23399_ch05.qxd 12/14/06 17:49 Page 262
  • 290. public void setRank(Rank r) { rank = r; } public Rank getRank( ) { return rank; } . . . } Notice how the enumerate type Rank is used in the setRank and getRank methods. It is treated just as any other types are. To access the Rank constants from outside of the Faculty class, we write Faculty.Rank.ASSISTANT Faculty.Rank.FULL and so forth. Here’s an example that assigns the rank of ASSISTANT to a Faculty object: Faculty prof = new Faculty(...); prof.setRank(Faculty.Rank.ASSISTANT); And here’s an example to retrieve the rank of a Faculty object: Faculty teacher; //assume 'teacher' is properly created Faculty.Rank rank; rank = teacher.getRank(); 5.7 Enumerated Constants 263 1. Define an enumerated type Day that includes the constants SUNDAY through SATURDAY. 2. What is the method that returns an enumerated constant, given the matching String value? 3. Detect the error(s) in the following code: enum Fruit {APPLE, ORANGE, BANANA} Fruit f1, f2; int f3; f1 = 1; f2 = ORANGE; f3 = f1; f1 = BANANA; wu23399_ch05.qxd 12/14/06 17:49 Page 263
  • 291. Sample Development Drawing Shapes Whenacertainperiodoftimepasseswithoutanyactivityonacomputer,ascreensaverbe- comes active and draws different types of geometric patterns or textual messages. In this section we will develop an application that simulates a screensaver.We will learn a development skill very commonly used in object-oriented programming. Whether we develop alone or as a project team member, we often find ourselves writing a class that needs to behave in a specific way so that it works correctly with other classes.The other classes may come from standard Java packages or could be developed by the other team members. In this particular case, we use the DrawingBoard class (written by the author). This is a helper class that takes care of programming aspects we have not yet mastered,such as moving multiple geometric shapes in a smooth motion across the screen.It is not an issue of whether we can develop this class by ourselves, because no matter how good we be- come as programmers,we would rarely develop an application completely on our own. We already used many predefined classes from the Java standard libraries, but the way we will use the predefined class here is different.When we developed programs before, the classes we wrote called the methods of predefined classes.Our main method creating a GregorianCalendar object and calling its methods is one example.Here,for us to use a predefined class, we must define another class that provides necessary services to this predefined class. Figure 5.14 differentiates the two types of predefined classes. The first type does not place any restriction other than calling the methods correctly, while the second type requires us to implement helper classes in a specific manner to support it. In our case, the predefined class DrawingBoard will require another class named DrawableShape that will assume the responsibility of drawing individual geometric shapes.So,to use the DrawingBoard class in our program,we must implement the class named DrawableShape. And we must implement the DrawableShape class in a specific way.The use of the DrawingBoard class dictates that we define a set of fixed methods in the DrawableShape class. We can add more, but we must at the minimum provide the specified set of fixed methods because the DrawingBoard class will need to call these methods.The methods are“fixed”in the method signature—method name,the number of parameters and their types,and return type—but the method body can be defined in any way we like.This is how the flexibility is achieved.For example,the DrawableShape class we define must include a method named draw with the dictated signature.But it’s up to us to decide what we put in the method body.So we can choose,for example,to implement the method to draw a circle,rectangle,or any other geometric shape of our choosing. As always,we will develop this program following incremental development steps. The incremental development steps we will take here are slightly different in character from those we have seen so far. In the previous incremental developments, we knew all the ingredients,so to speak.Here we have to include a step to explore the DrawingBoard class.We will find out shortly that to use the DrawingBoard class,we will have to deal with some Java standard classes we have not seen yet. Pedagogically, a textbook may try to 264 Chapter 5 Selection Statements 5.8 Sample Development wu23399_ch05.qxd 12/14/06 17:50 Page 264
  • 292. 5.8 Sample Development 265 Figure 5.14 Two types of predefined classes.The first type does not require us to do anything more than use the predefined classes by calling their methods.The second type requires us to define helper classes for the predefined classes we want to use. MyClass2 To use type 2 predefined classes, we must define helper classes required by the predefined classes. There's no restriction in using type 1 predefined classes other than calling their methods correctly. MyClass1 Type1Class Type2Class MyHelperClass A class we implement A class given to us explain beforehand everything that is necessary to understand the sample programs. But no textbook can explain everything.When we develop programs,there will always be a time when we encounter some unknown classes. We need to learn how to deal with such a situation in our development steps. Problem Statement Write an application that simulates a screensaver by drawing various geometric shapes in different colors.The user has the option of choosing a type (ellipse or rectangle),color,and movement (stationary,smooth,or random). Overall Plan We will begin with our overall plan for the development. Let’s begin with the outline of program logic.We first let the user select the shape, its movement, and its color, and we then start drawing.We express the program flow as having four tasks: 1. Get the shape the user wants to draw. 2. Get the color of the chosen shape. 3. Get the type of movement the user wants to use. 4. Start the drawing. program tasks wu23399_ch05.qxd 12/14/06 17:50 Page 265
  • 293. 5.8 Sample Development—continued 266 Chapter 5 Selection Statements Let’s look at each task and determine an object that will be responsible for handling the task. For the first three tasks, we can use our old friend Scanner. We will get into the details of exactly how we ask the users to input those values in the later incremental steps.For the last task of actually drawing the selected shape,we need to define our own class.The task is too specific to the program, and there is no suitable object in the stan- dard packages that does the job.As discussed earlier,we will use a given predefined class DrawingBoard and define the required helper class DrawableShape. We will define a top-level control object that manages all these objects.We will call this class Ch5DrawShape. As explained in Section 4.10, we will make this control object the main class.Here’s our working design document: Design Document: Ch5DrawShape Class Purpose Ch5DrawShape The top-level control object that manages other objects in the program.This is the main class,as explained in Section 4.10. DrawingBoard The given predefined class that handles the movement of DrawableShape objects. DrawableShape The class for handling the drawing of individual shapes. Scanner The standard class for handling input routines. program classes Ch5DrawShape Scanner DrawingBoard DrawableShape Figure 5.15 The program diagram for the Ch5DrawShape program. Figure 5.15 is the program diagram for this program. wu23399_ch05.qxd 12/14/06 17:50 Page 266
  • 294. 5.8 Sample Development 267 We will implement this program in the following six major steps: 1. Start with a program skeleton.Explore the DrawingBoard class. 2. Define an experimental DrawableShape class that draws a dummy shape. 3. Add code to allow the user to select a shape.Extend the DrawableShape and other classes as necessary. 4. Add code to allow the user to specify the color.Extend the DrawableShape and other classes as necessary. 5. Add code to allow the user to specify the motion type.Extend the DrawableShape and other classes as necessary. 6. Finalize the code by tying up loose ends. Our first task is to find out about the given class.We could have designed the input rou- tines first,but without knowing much about the given class,it would be difficult to design suitable input routines.When we use an unknown class,it is most appropriate to find out more about this given class before we plan any input or output routines. Just as the de- velopment steps are incremental, our exploration of the given class will be incremental. Instead of trying to find out everything about the class at once, we begin with the basic features and skeleton code. As we learn more about the given class incrementally, we extend our code correspondingly. Step 1 Development: Program Skeleton We begin the development with the skeleton main class.The main purpose in step 1 is to use the DrawingBoard class in the simplest manner to establish the launch pad for the development. To do so, we must first learn a bit about the given DrawingBoard class. Here’s a brief description of the DrawingBoard class. In a real-world situation, we would be finding out about the given class by reading its accompanying documentation or some other external sources. The documentation may come in form of online javadoc documents or reference manuals. development steps step 1 design DrawingBoard An instance of this class will support the drawing of DrawableShape objects.Shapes can be drawn at fixed stationary positions,at random positions,or in a smooth motion at the specified speed.The actual drawing of the individual shapes is done inside the DrawableShape class.The client programmer decides which shape to draw. public void addShape ( DrawableShape shape ) Adds shape to this DrawingBoard object.You can add an unlimited number of DrawableShape objects. (Continued) wu23399_ch05.qxd 12/14/06 17:50 Page 267
  • 295. 5.8 Sample Development—continued 268 Chapter 5 Selection Statements DrawingBoard (Continued) public void setBackground( java.awt.Color color ) Sets the background color of this DrawingBoard object to the designated color.The default background color is black. public void setDelayTime( double delay ) Sets the delay time between drawings to delay seconds.The smaller the delay time,the faster the shapes move.If the movement type is other than SMOOTH, then setting the delay time has no visual effect. public void setMovement( Movement type ) Sets the movement type to type.Class constants for three types of motion are Movement.STATIONARY—draw shapes at fixed positions, Movement.RANDOM—draw shapes at random positions,and Movement.SMOOTH—draw shapes in a smooth motion. public void setVisible( boolean state ) Makes this DrawingBoard object appear on or disappear from the screen if state is true or false, respectively. To simulate the screensaver, setting it visible will cause a maximized window to appear on the screen. public void start( ) Starts the drawing.If the window is not visible yet,it will be made visible before the drawing begins. Among the defined methods, we see the setVisible method is the one to make it appear on the screen. All other methods pertain to adding DrawableShape objects and setting the properties of a DrawingBoard object. We will explain the standard java.awt.Color class when we use the setBackground method in the later step. In this step, we will keep the code very simple by only making it appear on the screen. We will deal with other methods in the later steps. Our working design document for the Ch5DrawShape class is as follows: Design Document:The Ch5DrawShape Class Method Visibility Purpose constructor public Creates a DrawingBoard object. main public This is main method of the class. start public Starts the program by opening a DrawingBoard object. wu23399_ch05.qxd 1/25/07 13:50 Page 268
  • 296. Since this is a skeleton code,it is very basic.Here’s the code: 5.8 Sample Development 269 step 1 code The purpose of step 1 testing is to verify that a DrawingBoard object appears correctly on the screen. Since this is our first encounter with the DrawingBoard class, it is probable that we are not understanding its documentation fully and completely. We need to verify this in this step. When a maximized window with the black back- ground appears on the screen, we know the main class was executed properly. After we verify the correct execution of the step 1 program, we will proceed to imple- ment additional methods of Ch5DrawShape and gradually build up the required DrawableShape class. Step 2 Development: Draw a Shape In the second development step, we will implement a preliminary DrawableShape class and make some shapes appear on a DrawingBoard window. To draw shapes, we need to add them to a DrawingBoard window. And to do so, we need to define the step 1 test step 2 design /* Chapter 5 Sample Development: Drawing Shapes (Step 1) The main class of the program. */ class Ch5DrawShape { private DrawingBoard canvas; public Ch5DrawShape( ) { canvas = new DrawingBoard( ); } public void start( ) { canvas.setVisible(true); } public static void main(String[] args) { Ch5DrawShape screensaver = new Ch5DrawShape( ); screensaver.start(); } } wu23399_ch05.qxd 12/14/06 17:50 Page 269
  • 297. 5.8 Sample Development—continued 270 Chapter 5 Selection Statements DrawableShape class with the specified set of methods. Here are the required methods and a brief description of what to accomplish in them: Required Methods of DrawableShape public void draw(java.awt.Graphics) Draw a geometric shape on the java.awt.Graphics. The DrawingBoard window calls the draw method of DrawableShape objects added to it. public java.awt.Point getCenterPoint( ) Return the center point of this shape. public java.awt.Dimension getDimension( ) Return the bounding rectangle of this shape as a Dimension. public void setCenterPoint( java.awt.Point ) Set the center point of this shape.The DrawingBoard window calls the setCenterPoint method of DrawableShape objects to update their positions in the SMOOTH movement type. At this stage, the main task is for us to confirm our understanding of the require- ments in implementing the DrawableShape class.Once we get this confirmation,we can get into the details of the full-blown DrawableShape class. To keep the preliminary class simple, we draw three filled circles of a fixed size and color. The DrawableShape class includes a single data member centerPoint to keep track of the shape’s center point.If we fix the radius of the circles to 100 pixels,that is,the bounding rectangle is 200 pixels by 200 pixels, and the color to blue, then the draw method can be written as follows: public void draw(Graphics g) { g.setColor(Color.blue); g.fillOval(centerPoint.x-100, centerPoint.y-100, 200, 200); } Since the size is fixed, we simply return a new Dimension object for the getDimension method: public Dimension getDimension( ) { return new Dimension(200, 200); } For the setCenterPoint and getCenterPoint methods, we assign the passed para- meter to the data member centerPoint and return the current value of the data member centerPoint, respectively. 200 200 100 wu23399_ch05.qxd 12/14/06 17:50 Page 270
  • 298. We are now ready to modify the Ch5DrawShape class to draw three filled circles. We will implement this by modifying the start method. First we need to create three DrawableShape objects and add them to the DrawingBoard object canvas: DrawableShape shape1 = new DrawableShape(); DrawableShape shape2 = new DrawableShape(); DrawableShape shape3 = new DrawableShape(); shape1.setCenterPoint(new Point(250,300)); shape2.setCenterPoint(new Point(500,300)); shape3.setCenterPoint(new Point(750,300)); canvas.addShape(shape1); canvas.addShape(shape2); canvas.addShape(shape3); Then we set the motion type to SMOOTH movement, make the window appear on the screen,and start the drawing: canvas.setMovement(DrawingBoard.Movement.SMOOTH); canvas.setVisible(true); canvas.start(); Here’s the code for the preliminary DrawableShape class: 5.8 Sample Development 271 step 2 code import java.awt.*; /* Step 2: Add a preliminary DrawableShape class A class whose instances know how to draw themselves. */ class DrawableShape { private Point centerPoint; public DrawableShape( ) { centerPoint = null; } public void draw(Graphics g) { g.setColor(Color.blue); g.fillOval(centerPoint.x-100, centerPoint.y-100, 200, 200); } Data Members Constructors draw wu23399_ch05.qxd 12/14/06 17:50 Page 271
  • 299. 5.8 Sample Development—continued The Ch5DrawShape class now has the modified start method as designed (the rest of the class remains the same): 272 Chapter 5 Selection Statements public Point getCenterPoint( ) { return centerPoint; } public Dimension getDimension( ) { return new Dimension(200, 200); } public void setCenterPoint(Point point) { centerPoint = point; } } getCenterPoint getDimension setCenterPoint import java.awt.*; /* Chapter 5 Sample Development: Start drawing shapes (Step 2) The main class of the program. */ class Ch5DrawShape { . . . public void start( ) { DrawableShape shape1 = new DrawableShape(); DrawableShape shape2 = new DrawableShape(); DrawableShape shape3 = new DrawableShape(); shape1.setCenterPoint(new Point(250,300)); shape2.setCenterPoint(new Point(500,300)); shape3.setCenterPoint(new Point(750,300)); canvas.addShape(shape1); canvas.addShape(shape2); canvas.addShape(shape3); start wu23399_ch05.qxd 12/14/06 17:50 Page 272
  • 300. canvas.setMovement(DrawingBoard.Movement.SMOOTH); canvas.setVisible(true); canvas.start(); } . . . } 5.8 Sample Development 273 Now we run the program and verify the three bouncing circles moving around.To test other options of the DrawingBoard class,we will try the other methods with different parameters: Method Test Parameter setMovement Try both DrawingBoard.STATIONARY and DrawingBoard.RANDOM. setDelayTime Try values ranging from 0.1 to 3.0. setBackground Try several different Color constants such as Color.white, Color.red,and Color.green. We insert these testing statements before the statement canvas.setVisible(true); in the start method. Another testing option we should try is the drawing of different geometric shapes. We can replace the drawing statement inside the draw method from g.fillOval(centerPoint.x-100, centerPoint.y-100, 200, 200); to g.fillRect(centerPoint.x-100, centerPoint.y-100, 200, 200); or g.fillRoundRect(centerPoint.x-100, centerPoint.y-100, 200, 200, 50, 50); to draw a filled rectangle or a filled rounded rectangle,respectively. step 2 test wu23399_ch05.qxd 12/14/06 17:50 Page 273
  • 301. 5.8 Sample Development—continued Step 3 Development: Allow the User to Select a Shape Now that we know how to interact with the DrawingBoard class, we can proceed to develop the user interface portion of the program. There are three categories in which the user can select an option: shape, color, and motion.We will work on the shape selec- tion here and on the color and motion selection in the next two steps.Once we are done with this step,the next two steps are fairly straightforward because the idea is essentially the same. Let’s allow the user to select one of three shapes—ellipse, rectangle, and rounded rectangle—the shapes we know how to draw at this point.We can add more fancy shapes later. In what ways should we allow the user to input the shape? There are two possible alternatives:The first would ask the user to enter the text and spell out the shape,and the second would ask the user to enter a number that corresponds to the shape (1 for ellipse, 2 for rectangle,3 for rounded rectangle,e.g.).Which is the better alternative? We anticipate at least two problems with the first input style.When we need to get a user’s name,for example,there’s no good alternative other than asking the user to enter his or her name.But when we want the user to select one of the few available choices,it is cumbersome and too much of a burden for the user. Moreover, it is prone to mistyping. To allow the user to make a selection quickly and easily, we can let the user select one of the available choices by entering a corresponding number.We will list the choices with numbers 1,2,and 3 and get the user’s selection as follows: System.out.print(Selection: Enter the Shape numbern + 1 - Ellipse n + 2 - Rectangle n + 3 - Rounded Rectangle n); int selection = scanner.nextInt(); For getting the dimension of the shape, we accept the width and height values from the user. The values cannot be negative, for sure, but we also want to restrict the values to a certain range.We do not want the shape to be too small or too large. Let’s set the minimum to 100 pixels and the maximum to 500 pixels. If the user enters a value outside the acceptable range,we will set the value to 100.The input routine for the width can be written as follows: System.out.print(Enter the width of the shapen + between 100 and 500 inclusive: ); int width = scanner.nextInt(); if (width 100 || width 500) { width = 100; } The input routine for the height will work in the same manner. 274 Chapter 5 Selection Statements step 3 design design alternative 1 design alternative 2 wu23399_ch05.qxd 12/14/06 17:50 Page 274
  • 302. For getting the x and y values of the shape’s center point, we follow the pattern of getting the width and height values. We will set the acceptable range for the x value to 200 and 800,inclusive,and the y value to 100 and 600,inclusive. Our next task is to modify the DrawableShape class so it will be able to draw three different geometric shapes. First we change the constructor to accept the three input values: public DrawableShape(Type sType, Dimension sDim, Point sCenter) { type = sType; dimension = sDim; centerPoint = sCenter; } The variables type, dimension, and centerPoint are data members for keeping track of necessary information. Next,we define the data member constants as follows: public static enum Type {ELLIPSE, RECTANGLE, ROUNDED_RECTANGLE} private static final Dimension DEFAULT_DIMENSION = new Dimension(200, 200); private static final Point DEFAULT_CENTER_PT = new Point(350, 350); In the previous step,the draw method drew a fixed-size circle.We need to modify it to draw three different geometric shapes based on the value of the data member type. We can modify the method to public void draw(Graphics g) { g.setColor(Color.blue); drawShape(g); } with the private method drawShape defined as private void drawShape(Graphics g) { switch (type) { case ELLIPSE: //code to draw a filled oval comes here break; case RECTANGLE: //code to draw a filled rectangle comes here break; 5.8 Sample Development 275 wu23399_ch05.qxd 12/14/06 17:50 Page 275
  • 303. 5.8 Sample Development—continued case ROUNDED_RECTANGLE: //code to draw a filled rounded rectangle //comes here break; } } Here’s the modified main class Ch5DrawShape: 276 Chapter 5 Selection Statements step 3 code import java.awt.*; import java.util.*; /* Chapter 5 Sample Development: Handle User Input for Shape Type (Step 3) The main class of the program. */ class Ch5DrawShape { . . . public void start( ) { DrawableShape shape1 = getShape(); canvas.addShape(shape1); canvas.setMovement(DrawingBoard.SMOOTH); canvas.setVisible(true); canvas.start(); } private DrawableShape getShape( ) { DrawableShape.Type type = inputShapeType(); Dimension dim = inputDimension(); Point centerPt = inputCenterPoint(); DrawableShape shape = new DrawableShape(type, dim, centerPt); return shape; } start getShape wu23399_ch05.qxd 1/12/07 10:45 Page 276
  • 304. private DrawableShape.Type inputShapeType( ) { System.out.print(Selection: Enter the Shape numbern + 1 - Ellipse n + 2 - Rectangle n + 3 - Rounded Rectangle n); int selection = scanner.nextInt(); DrawableShape.Type type; switch (selection) { case 1: type = DrawableShape.Type.ELLIPSE; break; case 2: type = DrawableShape.Type.RECTANGLE; break; case 3: type = DrawableShape.Type.ROUNDED_RECTANGLE; break; default: type = DrawableShape.Type.ELLIPSE; break; } return type; } private Dimension inputDimension( ) { System.out.print(Enter the width of the shapen + between 100 and 500 inclusive: ); int width = scanner.nextInt(); if (width 100 || width 500) { width = 100; } System.out.print(Enter the height of the shapen + between 100 and 500 inclusive: ); int height = scanner.nextInt(); if (height 100 || height 500) { height = 100; } return new Dimension(width, height); } 5.8 Sample Development 277 inputShapeType inputDimension wu23399_ch05.qxd 12/14/06 17:51 Page 277
  • 305. 5.8 Sample Development—continued private Point inputCenterPoint( ) { System.out.print(Enter the x value of the center pointn + between 200 and 800 inclusive: ); int x = scanner.nextInt(); if (x 200 || x 800) { x = 200; } System.out.print(Enter the y value of the center pointn + between 100 and 500 inclusive: ); int y = scanner.nextInt(); if (y 100 || y 500) { y = 100; } return new Point(x, y); } . . . } 278 Chapter 5 Selection Statements inputCenterPoint Data Members The DrawableShape class is now modified to this: import java.awt.*; /* Step 3: Draw different shapes A class whose instances know how to draw themselves. */ class DrawableShape { public static enum Type {ELLIPSE, RECTANGLE, ROUNDED_RECTANGLE} private static final Dimension DEFAULT_DIMENSION = new Dimension(200, 200); wu23399_ch05.qxd 12/14/06 17:51 Page 278
  • 306. private static final Point DEFAULT_CENTER_PT = new Point(350, 350); private Point centerPoint; private Dimension dimension; private Type type; public DrawableShape(Type sType, Dimension sDim, Point sCenter) { type = sType; dimension = sDim; centerPoint = sCenter; } public void draw(Graphics g) { g.setColor(Color.blue); drawShape(g); } . . . public void setType(Type shapeType) { type = shapeType; } private void drawShape(Graphics g) { switch (type) { case ELLIPSE: g.fillOval(centerPoint.x - dimension.width/2, centerPoint.y - dimension.height/2, dimension.width, dimension.height); break; case RECTANGLE: g.fillRect(centerPoint.x - dimension.width/2, centerPoint.y - dimension.height/2, dimension.width, dimension.height); break; case ROUNDED_RECTANGLE: g.fillRoundRect(centerPoint.x - dimension.width/2, centerPoint.y - dimension.height/2, dimension.width, dimension.height, 5.8 Sample Development 279 Constructor draw setType drawShape wu23399_ch05.qxd 12/14/06 17:51 Page 279
  • 307. 5.8 Sample Development—continued (int) (dimension.width * 0.3), (int) (dimension.height * 0.3)); break; } } } 280 Chapter 5 Selection Statements Notice how we add code for handling the case when an invalid number is entered in the inputShapeType method.We use the default case to set the shape type to ELLIPSE if an invalid value is entered. In addition to handling the invalid entries, it is critical for us to make sure that all valid entries are handled correctly.For example,we cannot leave the type undefined or assigned to a wrong value when one of the valid data is entered. When we write a selection control statement,we must make sure that all possible cases are handled correctly. Now we run the program multiple times, trying various shape types, dimensions, and center points. After we verify that everything is working as expected, we proceed to the next step. Step 4 Development: Allow the User to Select a Color In the fourth development step,we add a routine that allows the user to specify the color of the selected shape.We adopt the same input style for accepting the shape type as in step 3.We list five different color choices and let the user select one of them by entering the corresponding number. We use a default color when an invalid number is entered. Analogous to the shape selection routine,we will add a method named inputColor to the Ch5DrawShape class.The structure of this method is identical to that of the input meth- ods, except the return type is Color. Using the inputColor method, we can define the getShape method as follows: private DrawableShape getShape( ) { DrawableShape.Type type = inputShapeType(); Dimension dim = inputDimension(); step 3 test step 4 design wu23399_ch05.qxd 12/14/06 17:51 Page 280
  • 308. Point centerPt = inputCenterPoint(); Color color = inputColor(); DrawableShape shape = new DrawableShape(type, dim, centerPt, color); return shape; } We make a small extension to the DrawableShape class by changing the con- structor to accept a color as its fourth argument and adding a data member to keep track of the selected color. Here’s the modified Ch5DrawShape class: 5.8 Sample Development 281 step 4 code import java.awt.*; import java.util.*; /* Chapter 5 Sample Development: Color selection (Step 4) The main class of the program. */ class Ch5DrawShape { . . . private DrawableShape getShape( ) { DrawableShape.Type type = inputShapeType(); Dimension dim = inputDimension(); Point centerPt = inputCenterPoint(); Color color = inputColor(); DrawableShape shape = new DrawableShape(type, dim, centerPt, color); return shape; } private Color inputColor( ) { System.out.print(Selection: Enter the Color numbern + 1 - Red n + 2 - Green n + 3 - Blue n + 4 - Yellow n + 5 - Magenta n); int selection = scanner.nextInt(); getShape inputColor wu23399_ch05.qxd 12/14/06 17:51 Page 281
  • 309. 5.8 Sample Development—continued Color color; switch (selection) { case 1: color = Color.red; break; case 2: color = Color.green; break; case 3: color = Color.blue; break; case 4: color = Color.yellow; break; case 5: color = Color.magenta; break; default: color = Color.red; break; } return color; } . . . } 282 Chapter 5 Selection Statements The DrawableShape class is now modified to this: import java.awt.*; /* Step 4: Adds the color choice A class whose instances know how to draw themselves. */ class DrawableShape { . . . private static final Color DEFAULT_COLOR = Color.BLUE; . . . Data Members wu23399_ch05.qxd 12/14/06 17:51 Page 282
  • 310. private Color fillColor; . . . public DrawableShape(Type sType, Dimension sDim, Point sCenter, Color sColor) { type = sType; dimension = sDim; centerPoint = sCenter; fillColor = sColor; } public void draw(Graphics g) { g.setColor(fillColor); drawShape(g); } . . . } 5.8 Sample Development 283 Constructor draw Now we run the program several times,each time selecting a different color,and we verify that the shape is drawn in the chosen color. After we verify the program, we move on to the next step. Step 5 Development: Allow the User to Select a Motion Type In the fifth development step, we add a routine that allows the user to select the motion type.We give three choices to the user: stationary, random, or smooth. The same design we used in steps 3 and 4 is applicable here, so we adopt it for the motion type selection also.Since we adopt the same design,we can ease into the coding phase. Here’s the modified main class Ch5DrawShape: step 4 test step 5 design step 5 code import java.awt.*; import java.util.*; /* Chapter 5 Sample Development: Color selection (Step 5) The main class of the program. */ class Ch5DrawShape { . . . wu23399_ch05.qxd 12/14/06 17:51 Page 283
  • 311. 5.8 Sample Development—continued public void start( ) { DrawableShape shape1 = getShape(); canvas.addShape(shape1); canvas.setMovement(inputMotionType()); canvas.setVisible(true); canvas.start(); } . . . private DrawingBoard.Movement inputMotionType( ) { System.out.print(Selection: Enter the Motion numbern + 1 - Stationary (no movement) n + 2 - Random Movement n + 3 - Smooth Movement n ); int selection = scanner.nextInt(); DrawingBoard.Movement type; switch (selection) { case 1: type = DrawingBoard.Movement.STATIONARY; break; case 2: type = DrawingBoard.Movement.RANDOM; break; case 3: type = DrawingBoard.Movement.SMOOTH; break; default: type = DrawingBoard.Movement.SMOOTH; break; } return type; } . . . } 284 Chapter 5 Selection Statements start inputMotionType No changes are required for the DrawableShape class,as the DrawingBoard class is the one responsible for the shape movement. wu23399_ch05.qxd 12/14/06 17:51 Page 284
  • 312. Now we run the program multiple times and test all three motion types. From what we have done, we can’t imagine the code we have already written in the earlier steps to cause any problems;but if we are not careful,a slight change in one step could cause the code developed from the earlier steps to stop working correctly (e.g.,erroneously reusing data members in newly written methods).So we should continue to test all aspects of the program diligently. After we are satisfied with the program, we proceed to the final step. Step 6 Development: Finalize We will perform a critical review of the program, looking for any unfinished method, inconsistency or error in the methods, unclear or missing comments, and so forth. We should also not forget to improve the program for cleaner code and better readability. Another activity we can pursue in the final step is to look for extensions. There are several interesting extensions we can make to the program. First is the morphing of an object. In the current implementation, once the shape is selected, it will not change. It would be more fun to see the shape changes; for example, the width and height of the shape’s dimension can be set to vary while the shape is drawn. Another interesting variation is to make a circle morph into a rectangle and morph back into a circle. Second is the drawing of multiple shapes. Third is the variation in color while the shape is drawn.Fourth is the drawing of a text (we“draw”a text on the Graphics context just as we draw geometric shapes). You can make the text scroll across the screen from right to left by setting the motion type of DrawingBoard to STATIONARY and updating the center point value within our DrawableShape class. All these extensions are left as exercises. Summary 285 step 5 test program review possible extensions • A selection control statement is used to alter the sequential flow of control. • The if and switch statements are two types of selection control. • The two versions of the if statement are if–then–else and if–then. • A boolean expression contains conditional and boolean operators and evaluates to true or false. • Three boolean operators in Java are AND (), OR (||), and NOT (!). • DeMorgan’s laws state !(P Q) and !P || !Q are equivalent and !(P || Q) and !P !Q are equivalent. • Logical operators and || are evaluated by using the short-circuit evaluation technique. • A boolean flag is useful in keeping track of program settings. • An if statement can be a part of the then or else block of another if statement to formulate nested if statements. • Careful attention to details is important to avoid illogically constructed nested if statements. S u m m a r y wu23399_ch05.qxd 12/14/06 17:51 Page 285
  • 313. • When the equality symbol == is used in comparing the variables of reference data type, we are comparing the addresses. • The switch statement is useful for expressing a selection control based on equality testing between data of type char, byte, short, or int. • The break statement causes the control to break out of the surrounding switch statement (note: also from other control statements introduced in Chap. 6). • The standard classes introduced in this chapter are 286 Chapter 5 Selection Statements java.awt.Graphics java.awt.Color java.awt.Point java.awt.Dimension K e y C o n c e p t s sequential execution control statements if statement boolean expressions relational operators selection statements nested if statements increment and decrement operators boolean operators switch statements break statements defensive programming content pane of a frame enumerated constants • The java.awt.Graphics class is used to draw geometric shapes. • The java.awt.Color class is used to set the color of various GUI components. • The java.awt.Point class is used to represent a point in two-dimensional space. • The java.awt.Dimension class is used to represent a bounding rectangle of geometric shapes and other GUI components. • The enumerated constants provide type safety and increase the program readability. E x e r c i s e s 1. Indent the following if statements properly. a. if (a == b) if (c == d) a = 1; else b = 1; else c = 1; b. if (a == b) a = 1; if (c == d) b = 1; else c = 1; c. if (a == b) {if (c == d) a = 1; b = 2; } else b = 1; d. if (a == b) { if (c == d) a = 1; b = 2; } else {b = 1; if (a == d) d = 3;} 2. Which two of the following three if statements are equivalent? a. if (a == b) if (c == d) a = 1; else b = 1; wu23399_ch05.qxd 12/14/06 17:51 Page 286
  • 314. b. if (a == b) { if (c == d) a = 1; } else b = 1; c. if (a == b) if (c == d) a = 1; else b = 1; 3. Evaluate the following boolean expressions. For each of the following expressions, assume x is 10, y is 20, and z is 30. Indicate which of the following boolean expressions are always true and which are always false, regardless of the values for x, y, or z. a. x 10 || x 10 b. x y y x c. (x y + z) (x + 10 = 20) d. z - y == x Math.abs(y - z) == x e. x 10 x 10 f. x y || y x g. !(x y + z) || !(x + 10 = 20) h. !(x == y)) (x != y) (x y || y x) 4. Express the following switch statement by using nested if statements. switch (grade) { case 10: case 9: a = 1; b = 2; break; case 8: a = 3; b = 4; break; default: a = 5; break; } 5. Write an if statement to find the smallest of three given integers without using the min method of the Math class. 6. Draw control flow diagrams for the following two switch statements. Exercises 287 switch (choice) { case 1: a = 0; break; case 2: b = 1; break; case 3: c = 2; break; default: d = 3; break; } switch (choice) { case 1: a = 0; case 2: b = 1; case 3: c = 2; default: d = 3; } wu23399_ch05.qxd 12/14/06 17:51 Page 287
  • 315. 7. Write an if statement that prints out a message based on the following rules: 288 Chapter 5 Selection Statements If the Total Points Are Message to Print 100 You won a free cup of coffee. 200 You won a free cup of coffee and a regular-size doughnut. 300 You won a free cup of coffee and a regular-size doughnut and a 12-oz orange juice. 400 You won a free cup of coffee and a regular-size dough- nut and a 12-oz orange juice and a combo breakfast. 500 You won a free cup of coffee and a regular-size doughnut and a 12-oz orange juice and a combo breakfast and a reserved table for one week. 8. Rewrite the following if statement, using a switch statement. selection = scanner.nextInt( ); if (selection == 0) System.out.println(You selected Magenta); else if (selection == 1) System.out.println(You selected Cyan); else if (selection == 2) System.out.println(You selected Red); else if (selection == 3) System.out.println(You selected Blue); else if (selection == 4) System.out.println(You selected Green); else System.out.println(Invalid selection); 9. At the end of movie credits you see the year movies are produced in Roman numerals, for example, MCMXCVII for 1997. To help the production staff determine the correct Roman numeral for the production year, write an applet or application that reads a year and displays the year in Roman numerals. Roman Numeral Number I 1 V 5 X 10 L 50 C 100 D 500 M 1000 Remember that certain numbers are expressed by using a “subtraction,” for example, IV for 4, CD for 400, and so forth. wu23399_ch05.qxd 12/14/06 17:51 Page 288
  • 316. 10. Write a program that replies either Leap Year or Not a Leap Year, given a year. It is a leap year if the year is divisible by 4 but not by 100 (for example, 1796 is a leap year because it is divisible by 4 but not by 100). A year that is divisible by both 4 and 100 is a leap year if it is also divisible by 400 (for example, 2000 is a leap year, but 1800 is not). 11. One million is 106 and 1 billion is 109 . Write a program that reads a power of 10 (6, 9, 12, etc.) and displays how big the number is (Million, Billion, etc.). Display an appropriate message for the input value that has no corresponding word. The table below shows the correspondence between the power of 10 and the word for that number. Power of 10 Number 6 Million 9 Billion 12 Trillion 15 Quadrillion 18 Quintillion 21 Sextillion 30 Nonillion 100 Googol 12. Write a program RecommendedWeightWithTest by extending the RecommendedWeight (see Exercise 8 on page 209). The extended program will include the following test: if (the height is between 140cm and 230cm) compute the recommended weight else display an error message 13. Extend the RecommendedWeightWithTest program in Exercise 12 by allowing the user to enter his or her weight and printing out the message You should exercise more if the weight is more than 10 lb over the ideal weight and You need more nourishment if the weight is more than 20 lb under the recommended weight. 14. Employees at MyJava Lo-Fat Burgers earn the basic hourly wage of $7.25. They will receive time-and-a-half of their basic rate for overtime hours. In addition, they will receive a commission on the sales they generate while tending the counter. The commission is based on the following formula: Exercises 289 Sales Volume Commission $1.00 to $99.99 5% of total sales $100.00 to $299.99 10% of total sales $300.00 15% of total sales Write an application that inputs the number of hours worked and the total sales and computes the wage. wu23399_ch05.qxd 12/14/06 17:51 Page 289
  • 317. 15. Using the DrawingBoard class, write a screensaver that displays a scrolling text message. The text messages moves across the window, starting from the right edge toward the left edge. Set the motion type to stationary, so the DrawingBoard does not adjust the position. You have to adjust the text’s position inside your DrawableShape. 16. Define a class called Triangle that is capable of computing the perimeter and area of a triangle, given its three sides a, b, and c, as shown below. Notice that side b is the base of the triangle. b c a 290 Chapter 5 Selection Statements Perimeter a b c Area s(s a )(s b )(s c ) where s a 2 b c The design of this class is identical to that for the Ch5Circle class from Section 5.1. Define a private method isValid to check the validity of three sides. If any one of them is invalid, the methods getArea and getPerimeter will return the constant INVALID_DIMENSION. 17. Modify the Ch5RoomWinner class so the three dorm lottery cards are drawn vertically. Make the code for drawing flexible by using the HEIGHT constant in determining the placement of three cards. Development Exercises For the following exercises, use the incremental development methodology to implement the program. For each exercise, identify the program tasks, create a design document with class descriptions, and draw the program diagram. Map out the development steps at the start. Present any design alternatives and justify your selection. Be sure to perform adequate testing at the end of each development step. 18. MyJava Coffee Outlet (see Exercise 25 from Chap. 3) decided to give discounts to volume buyers. The discount is based on the following table: Order Volume Discount 25 bags 5% of total price 50 bags 10% of total price 100 bags 15% of total price 150 bags 20% of total price 200 bags 25% of total price 300 bags 30% of total price wu23399_ch05.qxd 12/14/06 17:51 Page 290
  • 318. Each bag of beans costs $5.50. Write an application that accepts the number of bags ordered and prints out the total cost of the order in the following style: Number of Bags Ordered: 173 - $ 951.50 Discount: 20% - $ 190.30 Your total charge is: $ 761.20 19. Combine Exercises 18 and 25 of Chap. 3 to compute the total charge including discount and shipping costs. The output should look like the following: Number of Bags Ordered: 43 - $ 236.50 Discount: 5% - $ 11.83 Boxes Used: 1 Large - $1.80 2 Medium - $2.00 Your total charge is: $ 228.47 Note: The discount applies to the cost of beans only. 20. You are hired by Expressimo Delivery Service to develop an application that computes the delivery charge. The company allows two types of packaging—letter and box—and three types of service—Next Day Priority, Next Day Standard, and 2-Day. The following table shows the formula for computing the charge: Exercises 291 Package Next Day Next Day Type Priority Standard 2-Day Letter $12.00,up to 8 oz $10.50,up to 8 oz Not available Box $15.75 for the first $13.75 for the first $7.00 for the first pound.Add $1.25 pound.Add $1.00 pound. Add $0.50 for each additional for each additional for each additional pound over the first pound over the first pound over the first pound. pound. pound. The program will input three values from the user: type of package, type of service, and weight of the package. wu23399_ch05.qxd 12/14/06 17:51 Page 291
  • 319. 21. Ms. Latte’s Mopeds ‘R Us rents mopeds at Monterey Beach Boardwalk. To promote business during the slow weekdays, the store gives a huge discount. The rental charges are as follows: 292 Chapter 5 Selection Statements Moped Type Weekday Rental Weekend Rental 50cc Mopette $15.00 for the first 3 h, $30.00 for the first 3 h, $2.50 per hour after the $7.50 per hour after the first 3 h. first 3 h. 250cc Mohawk $25.00 for the first $35.00 for the first 3 h, 3 h,$3.50 per hour after $8.50 per hour after the the first 3 h. first 3 h. Write a program that computes the rental charge, given the type of moped, when it is rented (either weekday or weekend), and the number of hours rented. 22. Write an application program that teaches children how to read a clock. Use JOptionPane to enter the hour and minute. Accept only numbers between 0 and 12 for hour and between 0 and 59 for minute. Print out an appropriate error message for an invalid input value. Draw a clock that looks something like this: To draw a clock hand, you use the drawLine method of the Graphics class. The endpoints of the line are determined as follows: (ox K cos , oy K sin ) (ox, oy) Note: We subtract here because the y value in pixel coordinates for windows increases in the downward direction. wu23399_ch05.qxd 12/14/06 17:51 Page 292
  • 320. The value for constant K determines the length of the clock hand. Make the K larger for the minute hand than for the hour hand. The angle is expressed in radians. The angle min of the minute hand is computed as (90 Minute 6.0) 1 80 and the angle hr of the hour hand is computed as 90 Hour M 6 i 0 n . u 0 te 30.0 1 80 where Hour and Minute are input values. The values 6.0 and 30.0 designate the degrees for 1 min and 1 h (i.e., the minute hand moves 6 degrees in 1 min and the hour hand moves 30.0 degrees in 1 h). The factor 180 converts a degree into the radian equivalent. You can draw the clock on the content pane of a frame window by getting the content pane’s Graphic object as described in the chapter. Here’s some sample code: import javax.swing.*; import java.awt.*; //for Graphics ... JFrame win; Container contentPane; Graphics g; ... win = new JFrame(); win.setSize(300, 300); win.setLocation(100,100); win.setVisible(true); ... contentPane = win.getContentPane(); g = contentPane.getGraphics(); g.drawOval(50,50,200,200); 23. Extend the application in Exercise 22 by drawing a more realistic, better- looking clock, such as this one: 3 9 12 6 Exercises 293 wu23399_ch05.qxd 12/14/06 17:51 Page 293
  • 321. 24. After starting a successful coffee beans outlet business, MyJava Coffee Outlet is now venturing into the fast-food business. The first thing the management decides is to eliminate the drive-through intercom. MyJava Lo-Fat Burgers is the only fast-food establishment in town that provides a computer screen and mouse for its drive-through customers. You are hired as a freelance computer consultant. Write a program that lists items for three menu categories: entree, side dish, and drink. The following table lists the items available for each entry and their prices. Choose appropriate methods for input and output. Entree Side Dish Drink Tofu Burger $3.49 Rice Cracker $0.79 Cafe Mocha $1.99 Cajun Chicken $4.59 No-Salt Fries $0.69 Cafe Latte $1.99 Buffalo Wings $3.99 Zucchini $1.09 Espresso $2.49 Rainbow Fillet $2.99 Brown Rice $0.59 Oolong Tea $0.99 294 Chapter 5 Selection Statements wu23399_ch05.qxd 12/14/06 17:51 Page 294
  • 322. Repetition Statements O b j e c t i v e s After you have read and studied this chapter,you should be able to • Nest a loop repetition statement inside another repetition statement. • Choose the appropriate repetition control statement for a given task. • (Optional) Write simple recursive methods. • Format output values by using the Formatter class. 295 • Implement repetition control in a program using while statements. • Implement repetition control in a program using do–while statements. • Implement a generic loop-and-a-half repetition control statement. • Implement repetition control in a program using for statements. 6 wu23399_ch06.qxd 12/14/06 17:53 Page 295
  • 323. he selection statements we covered in Chapter 5 alter the control flow of a program. In this chapter we will cover another group of control statements called repetition statements. Repetition statements control a block of code to be executed for a fixed number of times or until a certain condition is met. We will describe Java’s three repetition statements: while, do–while, and for. Finally, in optional Section 6.11, we will describe recursive methods. A recursive method is a method that calls itself. Instead of a repetition statement, a recursive method can be used to program the repetition control flow. 6.1 The while Statement Suppose we want to compute the sum of the first 100 positive integers 1, 2, . . . , 100. Here’s how we compute the sum, using a while statement: int sum = 0, number = 1; while (number = 100) { sum = sum + number; number = number + 1; } Let’s analyze the while statement. The statement follows the general format while ( boolean expression ) statement where statement is either a single statement or a compound statement. The statement of the sample while statement is a compound statement and there- fore has the left and right braces. Repetition statements are also called loop state- ments, and we characterize the statement as the loop body. Figure 6.1 shows how this while statement corresponds to the general format. As long as the boolean expression is true, the loop body is executed. Figure 6.2 is a diagram showing the control flow of the sample code. Let’s modify the loop so this time we keep on adding the numbers 1, 2, 3, and so forth, until the sum becomes more than 1,000,000. Here’s how we write the while statement: int sum = 0, number = 1; while ( sum = 1000000 ) { sum = sum + number; number = number + 1; } 296 Chapter 6 Repetition Statements I n t r o d u c t i o n T while state- ment syntax loop body recursive method repetition statements wu23399_ch06.qxd 12/14/06 17:53 Page 296
  • 324. Notice how the boolean expression is modified, and it is the only part of the while statement that is modified. Let’s try another example. This time, we compute the product of the first 20 odd integers. (Note: The ith odd integer is 2 * i – 1. For example, the fourth odd integer is 2 * 4 – 1 = 7.) int product = 1, number = 1, count = 20, lastNumber; lastNumber = 2 * count - 1; while (number = lastNumber) { product = product * number; number = number + 2; } The first and the third sample while statements are called count-controlled loops because the loop body is executed for a fixed number of times (as if we were counting). 6.1 The while Statement 297 count- controlled loop number = 100 ) { while ( sum = sum + number; number = number + 1; } Boolean Expression Statement (loop body) Figure 6.1 Correspondence of the example while statement of the general format. Figure 6.2 A diagram showing the control flow of a while statement. true false number = 100? sum = sum + number; number = number + 1; int sum = 0, number = 1; wu23399_ch06.qxd 12/14/06 17:53 Page 297
  • 325. Improving User Interface with a Loop Now let’s study how the repetition control in the program will improve the user in- terface of the program. In earlier sample programs, we assumed the input data were valid. The programs we have written may produce wrong results or simply stop run- ning if the user enters an invalid value. Assuming that the input values are valid makes the writing of programs easier because we do not have to write code to han- dle the invalid values. Although it is easier for us to write such programs, it would be an inferior interface from the user’s standpoint. Requiring the user to make no mistake in entering input values is too restrictive and not user-friendly. We need to develop programs that are more user-friendly. Imagine you successfully entered 19 values, but on the 20th input value, you mistyped. A user-hostile program would stop, and you would have to run the program again. A more user-friendly program would allow you to reenter a correct 20th value. All we could have done using a selection statement was either to print out an error message or to set a default value if the user enters an invalid value. In the inputShapeType method of the Chapter 5 sample development, for example, if the user enters any invalid value, we set the shape type to ellipse. Instead of quitting the program after displaying an error message or continuing the program with a de- fault value, it would be better in general to allow the user to reenter the value until the correct value is entered. We need a repetition control to achieve this. Let’s look at an example. Suppose we want to input a person’s age, and the value must be between 0 and 130. We know the age cannot be negative, so the age input must be greater than or equal to 0. We set the upper bound to 130 to take into account the possibility of some long-living human beings in a remote hamlet in Timbuktu. Let’s say we will let the user enter the age until a valid age is entered. We can code this repetition control, using a while statement: Scanner scanner = new Scanner(System.in); int age; System.out.print(Your Age (between 0 and 130): ); age = scanner.nextInt(); while (age 70 || age 130) { System.out.println( An invalid age was entered. Please try again.); System.out.print (Your Age (between 0 and 130): ); age = scanner.nextInt(); } Notice that we included the statements System.out.print(Your Age (between 0 and 130): ); age = scanner.nextInt(); to input the age before the while statement. Without this input statement, the vari- able age will not have a value when the boolean expression is evaluated for the very 298 Chapter 6 Repetition Statements wu23399_ch06.qxd 12/14/06 17:53 Page 298
  • 326. first time. This reading of a value before the testing is done is called a priming read. We will discuss this issue of priming read further in Section 6.4. As the second example, let’s modify the inputShapeType method from Sec- tion 5.6. To refresh our memory, here’s the original code: private DrawableShape.Type inputShapeType( ) { System.out.print(Selection: Enter the Shape numbern + 1 - Ellipse n + 2 - Rectangle n + 3 - Rounded Rectangle n); int selection = scanner.nextInt(); DrawableShape.Type type; switch (selection) { case 1: type = DrawableShape.Type.ELLIPSE; break; case 2: type = DrawableShape.Type.RECTANGLE; break; case 3: type = DrawableShape.Type.ROUNDED_RECTANGLE; break; default: type = DrawableShape.Type.ELLIPSE; break; } return type; } To allow the user to reenter the value until the valid entry is made, we can modify the method to the following: private int inputShapeType( ) { int selection = getSelection(); DrawableShape.Type type; switch (selection) { case 1: type = DrawableShape.Type.ELLIPSE; break; case 2: type = DrawableShape.Type.RECTANGLE; break; case 3: type = DrawableShape.Type.ROUNDED_RECTANGLE; break; default: System.out.println (Internal Error: Proceed with Default); type = DrawableShape.Type.ELLIPSE; break; 6.1 The while Statement 299 priming read getSelection is defined after this method. This default case should never happen if getSelection is implemented correctly.We put this here to catch any internal coding error. wu23399_ch06.qxd 12/14/06 17:53 Page 299
  • 327. } return type; } private int getSelection( ) { int selection; System.out.print(Selection: Enter the Shape numbern + 1 - Ellipse n + 2 - Rectangle n + 3 - Rounded Rectangle n); selection = scanner.nextInt(); while (selection 1 || selection 3) { System.out.println( An invalid age was entered. Please try again.n); System.out.print(Selection: Enter the Shape numbern + 1 - Ellipse n + 2 - Rectangle n + 3 - Rounded Rectangle n); selection = scanner.nextInt(); } return selection; } The next example keeps reading in integers and computes their running sum until a negative number is entered. int sum = 0; number; Scanner scanner = new Scanner(System.in); System.out.print(Enter integer ); number = scanner.nextInt(); while (number = 0) { sum = sum + number; System.out.print(Enter integer ); number = scanner.nextInt(); } The previous three sample while statements are called sentinel-controlled loops. With a sentinel-controlled loop, the loop body is executed repeatedly until any one of the designated values, called a sentinel, is encountered. The sentinels for the three examples, respectively, are any value between 0 and 130, any value from 1 to 3, and any negative number. 300 Chapter 6 Repetition Statements sentinel- controlled loop wu23399_ch06.qxd 12/14/06 17:53 Page 300
  • 328. Sample Program with a Loop Let’s write a short sample program that illustrates the use of a while statement. It is a well-known fact that students in college do not get enough sleep, some studying hard while others are enjoying life too much. Which dorm they live in also makes a huge difference, so let’s develop a program that determines the average sleeping time of the residents in a given dorm. This information can be made available on the housing office website so the students can make an informed decision on which dorm to choose for the next academic year. Using Scanner, first we will input the dorm name. Then we loop and input the length of sleep of the residents until the input value of zero is entered. When the input is done, the average sleep time is displayed. We use zero as a sentinel value in- stead of a negative number such as –1 because we do not want to consider a zero as a valid entry. Here’s the program listing: 6.1 The while Statement 301 /* Chapter 6 Sample Program: Sleep Statistics for Dorm Residents File: Ch6SleepStatistics.java */ import java.text.*; import java.util.*; class Ch6SleepStatistics { private Scanner scanner; public static void main (String[] args) { Ch6SleepStatistics prog = new Ch6SleepStatistics( ); prog.start(); } public Ch6SleepStatistics() { scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); } public void start( ) { double sleepHour, sum = 0; int cnt = 0; //enter the dorm name System.out.print(Dorm name: ); String dorm = scanner.next(); //Loop: get hours of sleep for each resident // until 0 is entered. sleepHour = getDouble(Enter sleep hours (0 - to stop:); main start Constructor wu23399_ch06.qxd 12/14/06 17:53 Page 301
  • 329. while (sleepHour != 0) { sum += sleepHour; cnt++; sleepHour = getDouble(Enter sleep hours (0 - to stop):); } if (cnt == 0) { System.out.println (No Data Entered); } else { DecimalFormat df = new DecimalFormat(0.00); System.out.println( Average sleep time for + dorm + is nn + df.format(sum/cnt) + hours.); } } private double getDouble(String message) { double result; System.out.print(message); result = scanner.nextDouble(); return result; } } 302 Chapter 6 Repetition Statements getDouble Finding the Greatest Common Divisor Let’s close this section with a slightly more complicated example of using a loop statement. In Section 5.4, we defined the equals method for the Fraction class. We indicated that the fully functional equals method needs to call another method to re- duce a fraction to its simplest from (e.g., the simplest form of 16/32 is 1/2). To sim- plify a fraction, we need to find the greatest common divisor of its numerator and denominator. For example, the greatest common divisor of 16 and 32 is 16. Divid- ing both the numerator and the denominator by their greatest common denominator will reduce the fraction to its simplest form. Here we will define a method that returns the greatest common divisor of two given arguments. (Note: We will de- velop a full definition of the Fraction class in Chapter 7 when we introduce additional concepts on programmer-defined classes.) We will first provide a brute-force solution (inelegant) and then a clever solu- tion based on the Euclidean algorithm (elegant). The brute-force approach derives wu23399_ch06.qxd 12/14/06 17:53 Page 302
  • 330. the solution by applying the definition of greatest common divisor directly. Given two positive integers M and N, where M = N, we find their greatest common divi- sor by dividing M and N with values from 1 to M. The last integer that divided both M and N perfectly (i.e., there is no remainder), is the greatest common divisor. Consider 24 and 36, for example. The numbers that divide 24 and 36 perfectly are 1, 2, 3, 4, 6, and 12. So the greatest common divisor is 12. The fraction 24/36 is reduced to its simplest form 2/3 by dividing 24 and 36 by 12. We can see if a num- ber j divides another number i perfectly by using the modulo arithmetic. If i % j == 0, then j divides i perfectly because the remainder of the division is 0. Here’s the brute-force method: public int gcd_bruteforce(int m, int n) { //assume m, n = 1 int last = Math.min(m, n); int gcd; int i = 1; while (i = last) { if (m % i == 0 n % i == 0) { gcd = i; } i++; } return gcd; } Now let’s study an elegant solution based on the Euclidean algorithm. We begin with an example. Consider two positive integers 44 and 16. We will use the notation gcd(a, b) to stand for the greatest common divisor of a and b. Notice that gcd(44, 16) = 4. Here’s how the Euclidean algorithm works. First divide 44 by 16. The remainder is 12. We have the relation 44 = 2 * 16 + 12 From this, we can conclude that the greatest common divisor G that divides 44 and 16 must also divide 12. If it doesn’t, then we get a contradiction. If a number G can divide 16 perfectly but cannot divide 12 perfectly, then 44 % G = (2*16 + 12) % G will result in a nonzero value. This is a contradiction. So now we can reduce the problem of finding gcd(44, 16) to gcd(16, 12). We repeat the process. 16 = 1 * 12 + 4 Now we reduce the problem to gcd(12, 4). Since 12 = 3 * 4 + 0 6.1 The while Statement 303 wu23399_ch06.qxd 12/14/06 17:53 Page 303
  • 331. shows no remainder, we finish the process and return the answer 4 as the greatest common divisor. The sequence of reduction is gcd(44, 16) = gcd(16, 12) = gcd(12, 4) = 4. How do we translate this concept into a working code? Let’s map out the sequence of reductions graphically: From this diagram, we see that M at one stage becomes N in the next stage and the remainder R becomes M in the next stage. We repeat this process until the remain- der becomes 0. The value of M (4 in this example) at the end of the repetition is the greatest common divisor. Here’s the gcd method that implements this idea: public int gcd(int m, int n) { //it doesn't matter which of n and m is bigger //this method will work fine either way //assume m,n = 1 int r = n % m; while (r !=0) { n = m; m = r; r = n % m; } return m; } Here’s how we trace the repetition: gcd(44, 16) gcd(16, 12) gcd(12, 4) 44 % 16 12 16 % 12 4 12 % 4 0 N M R N % M 304 Chapter 6 Repetition Statements Repetition Count n m r 0 44 16 12 1 16 12 4 2 12 4 0 The first column indicates the number of times the while loop is executed. So the first row shows the values of n, m, and r after zero repetitions, that is, before the while statement is executed. The third row shows the values after the second repetition is wu23399_ch06.qxd 12/14/06 17:53 Page 304
  • 332. completed. At the point where the third repetition is attempted, the value of r is 0, so the while loop is terminated and the value of m, which is 4, is returned. The two versions of finding the greatest common denominator produce the correct results. If they both produce the same results, which version shall we prefer? The brute-force method is probably a lot easier to understand, at least initially, be- cause it reflects the definition of greatest common divisor clearly. We always prefer the one that is clearer and easier to understand, but only when their performances are relatively the same. In this example, the Euclidean gcd method far outperforms the gcd_bruteforce method. In other words, the Euclidean gcd method finds the solution much faster than gcd_bruteforce. And the gap widens dramatically when the values of M become large.We will analyze the performance of these two meth- ods experimentally by recording their execution times in Section 6.10. 6.2 Pitfalls in Writing Repetition Statements 305 1. Write a while statement to add numbers 11 through 20. Is this a count- controlled or sentinel-controlled loop? 2. Write a while statement to read in real numbers and stop when a negative number is entered. Is this a count-controlled or sentinel-controlled loop? 6.2 Pitfalls in Writing Repetition Statements No matter what you do with the while statement (and other repetition statements), make sure that the loop will eventually terminate. Watch out for an infinite loop such as this one: int product = 0; while (product 500000) { product = product * 5; } Do you know why this is an infinite loop? The variable product is multiplied by 5 in the loop body, so the value for product should eventually become larger than 500000, right? Wrong. The variable product is initialized to 0, so product remains 0. The boolean expression product 500000 will never be false, and therefore this while statement is an infinite loop. You have to make sure the loop body contains a statement that eventually makes the boolean expression false. Here’s another example of an infinite loop: int count = 1; while (count != 10) { count = count + 2; } Since the variable count is initialized to 1 and the increment is 2, count will never be equal to 10. Note: In theory, this while statement is an infinite loop, but infinite loop wu23399_ch06.qxd 12/14/06 17:53 Page 305
  • 333. in programming languages other than Java, this loop will eventually terminate because of an overflow error. An overflow error will occur if you attempt to assign a value larger than the maximum value the variable can hold. When an overflow error occurs, the execution of the program is terminated in almost all programming languages. With Java, however, an overflow will not cause program termination. When an overflow occurs in Java, a value that represents infinity (IEEE 754 infinity, to be precise) is assigned to a variable and no abnormal ter- mination of a program will occur. Also, in Java an overflow occurs only with float and double variables; no overflow will happen with int variables. When you try to assign a value larger than the maximum possible integer that an int variable can hold, the value “wraps around” and becomes a negative value. Whether the loop terminates or not because of an overflow error, the logic of the loop is still an infinite loop, and we must watch out for it. When you write a loop, you must make sure that the boolean expression of the loop will eventually become false. Another pitfall for you to avoid is the using of real numbers for testing and increment. Consider the following two loops: //Loop 1 double count = 0.0; while (count != 1.0) count = count + 0.333333333333333; //there are fifteen 3s //Loop 2 double count = 0.0; while (count != 1.0) count = count + 0.3333333333333333; //there are sixteen 3s The second while terminates correctly, but the first while is an infinite loop. Why the difference? Because only an approximation of real numbers can be stored in a com- puter. We know in mathematics that 1 3 1 3 1 3 is equal to 1. However, in a computer, an expression such as 1.0/3.0 + 1.0/3.0 + 1.0/3.0 may or may not get evaluated to 1.0, depending on how precise the approximation is. The problem here is not that the number 1/3 is a repeating decimal. A decimal number such as 0.1 cannot be stored precisely in a computer memory either. Con- sider the following example: double count = 0.0; while (count != 1.0) { count = count + 0.10; } 306 Chapter 6 Repetition Statements overflow error imprecise loop counter wu23399_ch06.qxd 12/14/06 17:53 Page 306
  • 334. This repetition statement looks simple enough. We initialize count to 0.0 and re- peatedly add 0.10 to it, so after 10 repetitions, the loop should terminate. Wrong. The counter variable count never becomes equal to 1.0. The closest it gets is 0.9999999999999999. Let’s change the loop to double count = 0.0; while (count = 1.0) { count = count + 0.10; System.out.println(count); } so we can see the values assigned to count. Here’s the output from this code: 0.1 0.2 0.30000000000000004 0.4 0.5 0.6 0.7 0.7999999999999999 0.8999999999999999 0.9999999999999999 1.0999999999999999 As these examples illustrate, we should avoid using real numbers as counter vari- ables because of the imprecision. 6.2 Pitfalls in Writing Repetition Statements 307 Another thing to watch out for in writing a loop is the off-by-1 error. Suppose we want to execute the loop body 10 times. Does the following code work? count = 1; while (count 10 ) { ... count++; } No, the loop body is executed 9 times. How about the following code? count = 0; while (count = 10 ) { ... count++; } Avoid using real numbers for counter variables as much as possible.If you use them,then be aware of the pitfall and ensure that the loop terminates. off-by-1 error wu23399_ch06.qxd 12/14/06 17:53 Page 307
  • 335. No, this time the loop body is executed 11 times. The correct while loop is count = 0; while (count 10 ) { ... count++; } or count = 1; while (count = 10 ) { ... count++; } Yes, we can write the desired loop as count = 1; while (count != 10 ) { ... count++; } but this condition for stopping the count-controlled loop is dangerous. We already mentioned about the potential trap of an infinite loop. In summary, 308 Chapter 6 Repetition Statements Watch out for the off-by-1 error (OBOE). To show you just how commonly the off-by-1 error occurs in everyday life, con- sider the following two questions.When you want to put a fencepost every 10 ft, how many posts do you need for a 100-ft fence? If it takes 0.5 s for an elevator to rise one floor, how long does it take to reach the fourth floor from the first level? The answers that come immediately are 10 posts and 2 s, respectively. But after a little more thought, we realize the correct answers are 11 posts (we need the final post at the end) and 1.5 s (there are three floors to rise to reach the fourth floor from the first level). Another common mistake made by beginning programmers is the inclusion of avoidable test in a loop. Consider the following loop statement: int oddSum = 0; int evenSum = 0; int num = 1; wu23399_ch06.qxd 12/14/06 17:53 Page 308
  • 336. while (num 1001) { if (num / 2 == 0) { //even # evenSum = evenSum + num; } else { //odd # oddSum = oddSum + num; } num = num + 2; } The code computes the sum of even numbers and the sum of odd numbers between 1 and 1000, inclusive. To compute the two sums, the if test is executed 1000 times. Is it necessary? No. We can compute the two sums more efficiently by writing two separate loops: int oddSum = 0; int evenSum = 0; int num = 1; while (num 1001) { oddSum = oddSum + num; num = num + 2; } num = 2; while (num 1001) { evenSum = evenSum + num; num = num + 2; } We can improve the code even further by usign only one loop as follows: int oddSum = 0; int evenSum = 0; int num = 1; while (num 1001) { oddSum = oddSum + num; evenSum = evenSum + (num + 1); num = num + 2; } 6.2 Pitfalls in Writing Repetition Statements 309 This test can be avoided by writing two loops. wu23399_ch06.qxd 12/14/06 17:53 Page 309
  • 337. And here are the points for you to remember in writing a loop. 310 Chapter 6 Repetition Statements The checklist for the repetition control: 1. Make sure the loop body contains a statement that will eventually cause the loop to terminate. 2. Make sure the loop repeats exactly the correct number of times. 3. If you want to execute the loop body N times,then initialize the counter to 0 and use the test condition counter N or initialize the counter to 1 and use the test condition counter N. 1. Which of the following is an infinite loop? a. int sum = 0, i = 0; while ( i = 0 ) { sum += i; i++; } b. int sum = 0, i = 100; while ( i != 0 ) { sum += i; i--; } 2. For each of the following loop statements, determine the value of sum after the loop is executed. a. int count = 0, sum = 0; while ( count 10 ) { sum += count; count++; } b. int count = 1, sum = 0; while ( count = 30 ) { sum += count; count += 3; } c. int count = 0, sum = 0; while ( count 20 ) { sum += 3*count; count += 2; } wu23399_ch06.qxd 12/14/06 17:53 Page 310
  • 338. 6.3 The do–while Statement The while statement is characterized as a pretest loop because the test is done before execution of the loop body. Because it is a pretest loop, the loop body may not be ex- ecuted at all. The do–while is a repetition statement that is characterized as a posttest loop. With a posttest loop statement, the loop body is executed at least once. The general format for the do–while statement is do statement while (boolean expression ) ; The statement is executed until the boolean expression becomes false. Remember that statement is either a single statement or a compound state- ment. We will adopt the same policy for the if statement; that is, we will use the syntax of compound statement even if there is only one statement in the loop body. In other words, we will use the left and right braces even if the loop body contains only one statement. Let’s look at a few examples. We begin with the second example from Sec- tion 6.1, which adds the whole numbers 1, 2, 3, . . . until the sum becomes larger than 1,000,000. Here’s the equivalent code in a do–while statement: int sum = 0, number = 1; do { sum += number; number++; } while ( sum = 1000000 ); Figure 6.3 shows how this do–while statement corresponds to the general format, and Figure 6.4 is a diagram showing the control flow of this do–while statement. Let’s rewrite the routine that inputs a person’s age by using the do–while state- ment. Here’s our first attempt: do { System.out.print(Your Age (between 0 and 130): ); age = scanner.nextInt(); } while (age 0 || age 130); It works, but unlike the version using the while statement, the code does not display an error message. The user could be puzzled as to why the input is not accepted. Suppose the user tries to enter 130 but actually enters 139 unintentionally. Without an error message to inform the user that the input was invalid, he or she may won- der why the program is asking again for input. A program should not be confusing to the user. We must strive for a program with a user-friendly interface. 6.3 The do–while Statement 311 do-while statement do–while syntax pretest loop posttest loop wu23399_ch06.qxd 12/14/06 17:53 Page 311
  • 339. To display an error message, we rewrite the do–while statement as do { System.out.print(Your Age (between 0 and 130): ); age = scanner.nextInt(); if (age 0 || age 130) { System.out.println( An invalid age was entered. Please try again.); } while (age 0 || age 130); This code is not as good as the version using the while statement. Do you know why? This do–while statement includes an if statement inside its loop body. Since the loop body is executed repeatedly, it is important not to include any extraneous statements. The if statement is repeating the same boolean expression of the do–while. Duplicating the testing conditions tends to make the loop statement harder to understand. For this example, we can avoid the extra test inside the loop 312 Chapter 6 Repetition Statements while ( sum = 1000000 ); } do { sum += number; number++; Boolean Expression Statement (loop body) Figure 6.3 Correspondence of the example do–while statement to the general format. true false sum = 1000000? sum += number; number++; int sum = 0, number = 1; Figure 6.4 A diagram showing the control flow of the do–while statement. wu23399_ch06.qxd 12/14/06 17:53 Page 312
  • 340. body and implement the control flow a little more clearly by using a while state- ment. In general, the while statement is more frequently used than the do–while statement. However, the while statement is not universally better than the do–while statement. It depends on a given task, and our job as programmers is to use the most appropriate one. We choose the repetition statement that implements the control flow clearly, so the code is easy to understand. When you have multiple conditions to stop the loop and you need to execute different responses to each of the multiple conditions, then the use of boolean variables often clarifies the meaning of the loop statement. Consider the following example. Suppose we need to compute the sum of odd integers entered by the user. We will stop the loop when the sentinel value 0 is entered, an even integer is en- tered, or the sum becomes larger than 1000. Without using any boolean variables, we can write this loop as follows: sum = 0; do { System.out.print(Enter integer: ); num = scanner.nextInt(); if (num == 0) { //sentinel System.out.print(Sum = + sum); } else if (num % 2 == 0) //invalid data System.out.print(Error: even number was entered); } else { sum += num; if (sum 1000) { //pass the threshold System.out.print(Sum became larger than 1000); } } } while ( !(num % 2 == 0 || num == 0 || sum 1000) ); The ending condition is tricky. We need to stop the loop if any one of the three conditions num % 2 == 0, num == 0, or sum 1000 is true. So we repeat the loop when none of the three conditions are true, which is expressed as !(num % 2 == 0 || num == 0 || sum 1000) We can also state the condition as do { ... } while( num % 2 != 0 num != 0 sum = 1000 ); which means “repeat the loop while num is odd and num is not 0 and sum is less than or equal to 1000.” Regardless of the method used, the test conditions are duplicated inside the loop body and in the boolean expression. 6.3 The do–while Statement 313 boolean vari- able and loop Note: !(a || b) is equal to (!a !b) wu23399_ch06.qxd 12/14/06 17:53 Page 313
  • 341. Set the variable to false so the loop terminates. Now, by using a boolean variable, the loop becomes 314 Chapter 6 Repetition Statements Note: continue is a reserved word in Java, while repeat is not. boolean repeat = true; sum = 0; do { System.out.print(Enter integer: ); num = scanner.nextInt(); if (num % 2 == 0) { //invalid data System.out.print(Error: even number was entered); repeat = false; } else if (num == 0) { //sentinel System.out.print(Sum = + sum); repeat = false; } else { sum += num; if (sum 1000) { //pass the threshold System.out.print(Sum became larger than 1000); repeat = false; } } } while ( repeat ); This loop eliminates duplicate tests. The use of boolean variables is helpful in mak- ing loop statements readable, especially when the loop has multiple stop conditions. As the last example of this section, here’s the gcd method implemented by using the do–while statement (we’ll call it gcd_do to differentiate it from other versions): public int gcd_do(int m, int n) { //it doesn't matter which of n and m is bigger //this method will work fine either way //assume m,n = 1 int r; do { r = n % m; n = m; m = r; } while (r != 0); return n; //NOTE: we're returning n, not m // because m == r == 0 after the loop } wu23399_ch06.qxd 12/14/06 17:53 Page 314
  • 342. 6.4 Loop-and-a-Half Repetition Control 315 1. Write a do–while loop to compute the sum of the first 30 positive odd integers. 2. Rewrite the following while loops as do–while loops. a. int count = 0, sum = 0; while ( count 10 ) { sum += count; count++; } b. int count = 1, sum = 0; while ( count = 30 ) { sum += count; count += 3; } loop-and-a-half control 6.4 Loop-and-a-Half Repetition Control When we compare the while and do–while repetition control, we realize the key dif- ference is the position of the testing relative to the loop body. The while loop tests the terminating condition before the loop body, but the do–while tests the terminat- ing condition after the loop body. What happens when we want to test the terminat- ing condition right in the middle of the loop body? Such repetition control can be characterized as a loop-and-a-half control because only the top half of the loop body is executed for the last repetition. Do we ever need such a looping statement? Consider the following while loop with the priming read: String name; System.out.print(Your name: ); name = scanner.next(); while (name.length() == 0) { System.out.println(Invalid entry. + You must enter at least one character.); System.out.print(Your name: ); name = scanner.next(); } Because the while loop tests the terminating condition at the beginning, we must place some statements before the loop to ensure the condition can be evaluated. The same statements are repeated inside the loop, so the terminating condition can be evaluated correctly after each repetition. This duplication of the statements can become tedious depending on what is to be duplicated. We can avoid the duplication wu23399_ch06.qxd 12/14/06 17:53 Page 315
  • 343. of code with the loop-and-a-half structure. Java does not support any special re- served word for the loop-and-a-half repetition control. Rather, we implement it using the while, if, and break reserved words. Here’s how we express the sample priming read while loop in a loop-and-a-half format: String name; while (true) { System.out.print(Your name: ); name = scanner.next(); if (name.length() == 0) break; System.out.println(Invalid entry. + You must enter at least one character. ); } We have seen the use of the break statement in Chapter 5. Execution of the break statement causes the control to jump out of the switch statement. We can in fact use the break statement with any control statement. In this example, the break statement causes the control to jump out of the while statement. Since it is executed when the if test is true, the String variable name contains at least one character. If the test fails, the next statement is executed and the control loops back to the top of the while loop. Expressing this control flow in a flowchart will result in the one shown in Figure 6.5. There are two concerns when we use the loop-and-a-half control. The first is the danger of an infinite loop. Notice the boolean expression of the while state- ment is simply true, which, of course, will always evaluate to true. So, if we forget to include an if statement to break out of the loop, it will end up in an infinite loop. 316 Chapter 6 Repetition Statements If the test evaluates to true, then jump out of the loop. false true name.length() 0? name = scanner.next(); System.out.println(...); Figure 6.5 A diagram showing the control flow of a loop-and-a-half statement. wu23399_ch06.qxd 12/14/06 17:53 Page 316
  • 344. The second concern is the complexity of multiple exit points. It is possible to write a loop-and-a-half statement with multiple break statements, something like this: while (true) { ... if (condition 1) break; ... if (condition 2) break; ... if (condition 3) break; ... } It gets tricky to write a correct control loop with multiple exit points. One of the frequently cited software engineering practices for reliable code is to enforce the one-entry one-exit control flow. In other words, there is one entry point to the loop and one exit point from the loop. With the standard while and do–while with no break statements inside the loop, we have this one-entry one-exit control flow. A loop-and-a-half control with multiple break statements, however, violates it. If we watch out for these two points, a loop-and-a-half control can be quite handy and can make the code more readable. Here are the things to remember in using the loop-and-a-half control. 6.4 Loop-and-a-Half Repetition Control 317 one-entry one-exit control The checklist for the loop-and-a-half control: 1. To avoid an infinite loop, make sure the loop body contains at least one if statement that breaks out of the loop. 2. To keep the control simple and easy to read, avoid using multiple if statements that break out of the loop. 3. Make sure the loop is short to keep the control logic as self-evident as possible. (Notice this applies to all loop statements,but more so for a loop-and-a-half.) In this textbook, we will be using loop-and-a-half statements whenever ap- propriate, that is, whenever it makes the code more readable and clearer. Before we conclude this section, here’s another loop-and-a-half statement. The loop evaluates the average score, and it terminates when the input is a negative number. int cnt = 0; double score, sum = 0.0; while (true) { System.out.print(Enter score: ); score = scanner.nextDouble(); wu23399_ch06.qxd 12/14/06 17:53 Page 317
  • 345. if (score 0) break; sum += score; cnt++; } if (cnt 0) { avg = sum / cnt; } else { //error: no input } Again, we will use the gcd method as the last example. Here’s the gcd method using the loop-and-a-half repetition control (we’ll call this version gcd_LaH): public int gcd_LaH(int m, int n) { //it doesn't matter which of n and m is bigger //this method will work fine either way //assume m,n = 1 int r; while (true) { r = n % m; if (r == 0) break; n = m; m = r; } return m; } 318 Chapter 6 Repetition Statements 1. Translate the following while loop to a loop-and-a-half format. int sum = 0, num = 1; while (num = 50) { sum += num; num++; } 2. Translate the following do–while loop to a loop-and-a-half format. int sum = 0, num = 1; do { sum += num; num++; } while (sum = 5000); wu23399_ch06.qxd 12/14/06 17:53 Page 318
  • 346. 6.5 The for Statement 319 control variable ; ; ) { i = 100 for ( sum += i; } i = 1 i++ Initialization Update Boolean Expression Statement (loop body) Figure 6.6 Correspondence of the example for statement to the general format. 6.5 The for Statement The for statement is the third repetition control statement and is especially suitable for count-controlled loops. Let’s begin with an example. The following code com- putes the sum of the first 100 positive integers: int i, sum = 0; for (i = 1; i = 100; i++) { sum += i; //equivalent to sum = sum + i; } The general format of the for statement is for ( initialization; boolean expression; update ) statement Figure 6.6 shows the correspondence of the sample code above to the general format. The diagram in Figure 6.7 shows how this statement is executed. The vari- able i in the statement is called a control variable, and it keeps track of the number true statement boolean expression initialization (loop body) increment false i = 100? i = 1; sum += i; i++; Figure 6.7 A diagram showing the control flow of the example for statement. wu23399_ch06.qxd 12/14/06 17:53 Page 319
  • 347. of repetitions. In the sample code, the control variable i is first initialized to 0, and immediately the boolean expression is evaluated. If the evaluation results in true, the loop body is executed. Otherwise, the execution of the for statement is terminated, and the control flows to the statement following this for statement. Every time the loop body is executed, the increment operator (i++) is executed and then the boolean expression is evaluated. The initialization component also can include a declaration of the control variable. We can do something like this for (int i = 1; i = 100; i++) instead of int i; for (i = 0; i 10; i++) The control variable may be initialized to any value, although it is almost always 0 or 1. The update expression in the example increments the control variable by 1. We can increment it with values other than 1, including negative values, for example, for (int i = 0; i 100; i += 5) //i = 0, 5, 10, . .. , 95 for (int j = 2; j 40; j *= 2)//j = 2, 4, 8, 16, 32 for (int k = 100; k 0; k--) //k = 100, 99, 98, 97, ..., 1 Notice that the control variable appears in all three components: initialization, conditional expression, and update. A control variable does not have to appear in all three components, but this is the most common style. Many other variations are allowed for these three components, but for novices, it is safer to use this style exclusively. Let’s look at an example from physics. When an object is dropped from height H, the position P of the object at time t can be determined by the formula P 16t2 H For example, if a watermelon is dropped from the roof of a 256-ft-high dormitory, it will drop like this: 256 ft at t 0 240 ft at t 1 192 ft at t 2 112 ft at t 3 0 ft at t 4 320 Chapter 6 Repetition Statements wu23399_ch06.qxd 12/14/06 17:53 Page 320
  • 348. We can use a for statement to compute the position P at time t. We will input the initial height and compute the position every second. We repeat this computa- tion until the watermelon touches the ground. The time the watermelon touches the ground is derived by solving for t when P 0. 0 16t2 H t 1 H 6 6.5 The for Statement 321 /* Chapter 6 Sample Program: Dropping a Watermelon File: Ch6DroppingWaterMelon.java */ import java.util.*; class Ch6DroppingWaterMelon { public static void main( String[] args ) { double initialHeight, position, touchTime; Scanner scanner = new Scanner(System.in); System.out.print(Initial Height:); initialHeight = scanner.nextDouble(); touchTime = Math.sqrt(initialHeight / 16.0); touchTime = Math.round(touchTime * 10000.0) / 10000.0; //convert to four decimal places System.out.println(nn Time t Position at Time t n); for (int time = 0; time touchTime; time++) { position = -16.0 * time*time + initialHeight; System.out.print( + time); System.out.println( + position); } //print the last second System.out.println( + touchTime + 0.00); } } wu23399_ch06.qxd 12/14/06 17:53 Page 321
  • 349. 322 Chapter 6 Repetition Statements Figure 6.8 The positions of a watermelon dropped from a height of 500 ft. Java 5.0 introduces a new form of the for statement.There is no formal name for the newest for loop, but the name for-each loop is used most often. The for-each loop is a very convenient way to iterate over a collection of items. We will introduce the new for loop in Chapter 10 and see its use in the data structure chapters. The format for the for loop presented in this section is the most basic version. The Java language allows more complex for statements. For instance, the initialization and update parts of the for statement are not limited to a single statement. They can contain zero or more statements. The following two state- ments,for example,are both valid. int val, i, j; for (i = 0, j = 100, val = 0; //init i 100 j 50; //bool expr i++, j--) { //increment val += i - j; } System.out.println(val = + val); Scanner scanner = new Scanner(System.in); Running the program with the input value 500.0 for the initial height and using System.out as output will result in the window shown in Figure 6.8. wu23399_ch06.qxd 12/14/06 17:53 Page 322
  • 350. 6.5 The for Statement 323 We have introduced three forms of repetition statements—while, do–while, and for. They are equivalent in their expressive power.In other words,a loop written in one form of repetition statement can be written by using the other two forms of repetition statement.Although they are equivalent,in many cases one form would express the repetition control in a more natural and direct manner. It is your responsibility as a programmer to implement the repetition control using the most appropriate form. int sum, cnt, n; for (sum = 0, cnt = 0; //init cnt 10; //bool expr //increment System.out.print(Enter number: ), n = scanner.nextInt(), sum += n, cnt++ ) { } Do you ever need to write such intricate for statements? Most likely, no.The two sample statements can be written more clearly and logically in other ways. We strongly recommend that you stick to the basic, and most logical, form of the for statement. 1. Write a for loop to compute the following. a. Sum of 1, 2, . . . , 100 b. Sum of 2, 4, . . . , 500 c. Product of 5, 10, . . . , 50 2. Rewrite the following while loops as for statements. a. int count = 0, sum = 0; while ( count 10 ) { sum += count; count++; } b. int count = 1, sum = 0; while ( count = 30 ) { sum += count; count += 3; } wu23399_ch06.qxd 12/14/06 17:53 Page 323
  • 351. inner for outer for 6.6 Nested for Statements In many processing tasks, we need to place a for statement inside another for state- ment. In this section, we introduce a simple nested for statement. We will see more examples of nested for statements later in the book, especially in Chapter 10 on array processing. Suppose we want to display a quick reference table for clerks at the Rugs-R- Us carpet store. The table in Figure 6.9 lists the prices of carpets ranging in size from 11 5 ft to 20 25 ft (using System.out for output). The width of a carpet ranges from 11 to 20 ft with an increment of 1 ft. The length of a carpet ranges from 5 to 25 ft with an increment of 5 ft. The unit price of a carpet is $19 per square foot. We use a nested for statement to print out the table. Let’s concentrate first on printing out prices. We’ll worry about printing out length and width values later. The following nested for statement will print out the prices: int price; for (int width = 11; width = 20; width++) { for (int length = 5; length = 25; length += 5) { price = width * length * 19; //$19 per sq ft. System.out.print( + price); } //finished one row; now move on to the next row System.out.println(); } 324 Chapter 6 Repetition Statements Figure 6.9 The price table for carpets ranging in size from 11 5 ft to 20 25 ft whose unit price is $19 per square foot. Length Width wu23399_ch06.qxd 12/14/06 17:53 Page 324
  • 352. Added statements The outer for statement is set to range from the first row (width = 11) to the last row (width = 20). For each repetition of the outer for, the inner for statement is executed, which ranges from the first column (length = 5) to the fifth column (length = 25). The loop body of the inner for computes the price of a single carpet size and prints out this price. So the complete execution of the inner for, which causes its loop body to be executed 5 times, completes the output of one row. The following shows the sequence of values for the two control variables. width length 11 5 10 15 20 25 12 5 10 15 20 25 13 5 10 . . . Now let’s add the code to print out the row and column index values for width and length. int price; System.out.print( 5 10 15 20 25); System.out.print(nn); for (int width = 11; width = 20; width++) { System.out.print(width + ); for (int length = 5; length = 25; length += 5) { price = width * length * 19; //$19 per sq ft. System.out.print( + price); } //finished one row; now move on to the next row System.out.print(n); } 6.6 Nested for Statements 325 Completes the printing of the first row Completes the printing of the second row wu23399_ch06.qxd 12/14/06 17:53 Page 325
  • 353. The next improvement is to include the labels Width and Length in the output. This enhancement is left as Exercise 19 at the end of the chapter. Also, in the ex- ample, literal constants are used for the carpet sizes and the increment value on length (11, 20, 5, 25, and 5), but in a real program, named constants should be used. 326 Chapter 6 Repetition Statements 1. What will be the value of sum after the following nested for loops are executed? a. int sum = 0; for (int i = 0; i 5; i++) { sum = sum + i; for (int j = 0; j 5; j++) { sum = sum + j; } } b. int sum = 0; for (int i = 0; i 5; i++) { sum = sum + i; for (int j = i; j 5; j++) { sum = sum + j; } } 2. What is wrong with the following nested for loop? int sum = 0; for (int i = 0; i 5; i++) { sum = sum + i; for (int i = 5; i 0; i--) { sum = sum + j; } } 6.7 Formatting Output In the table shown in Figure 6.10, the values are aligned very nicely. We purposely selected the unit price and the ranges of width and length so that the table output would look good. Notice that the output values are all four-digit numbers. Realisti- cally, we cannot expect output values to be so uniform. Let’s change the unit price to $15 and the range of widths to 5 through 14 ft and see what happens. The result is shown in Figure 6.10, which is not as neat as the previous output. What we need is a way to format the output so the values are printed out with the proper alignment. In the code, we used the fixed number of spaces between the values, and it worked because the output values have the same number of digits. To align the val- ues with a varying number of digits, we must vary the number of spaces in front of the values, as shown in Figure 6.11. The basic idea of formatted output is to allocate the same amount of space for the output values and align the values within the allocated space. We call the space wu23399_ch06.qxd 12/14/06 17:53 Page 326
  • 354. occupied by an output value the field and the number of characters allocated to a field its field width. In Figure 6.11, the field width is 6. We have already used two formatting classes—DecimalFormat and Simple- DateFormat—introduced in Chapters 2 and 3. The most recent version of Java SDK 1.5 has a new general-purpose formatting class called Formatter that includes the functionalities of DecimalFormat and SimpleDateFormat. For its power, using the Formatter class is slightly more complicated than using the DecimalFormat and SimpleDateFormat classes. To format output using Formatter, first we create its instance by passing the destination of the output as an argument. Suppose we want to send the formatted output to System.out; then we create a Formatter object as follows: Formatter formatter = new Formatter(System.out); Next we call its format method to output the formatted values. For example, to out- put an integer with the field width of 6, we write int num = 467; formatter.format(%6d, num); 6.7 Formatting Output 327 Figure 6.10 The price table for carpets with $15 per square foot and width ranging from 5 through 14 ft. Figure 6.11 How to place a varying number of spaces to align the output values.Hyphen is used here to indicate the blank space. field – – – ––3 – – – 445 Each value occupies six spaces. If the value has three digits, we put three blank spaces in front. If the value has four digits, we put two blank spaces in front, and so forth. – – – –34 – – – 339 – – 5684 – – –234 – – – –98 – – – 453 – – –231 – – 3444 wu23399_ch06.qxd 12/14/06 17:53 Page 327
  • 355. The string %6d is called a control string, and it directs how the formatting will take place. The value 6 specifies the field width, and the control character d indicates the output value is a decimal integer. The general syntax for the format method is as follows: format(control string, expr1, expr2, ...) The first argument is the control string, and it is followed by zero or more expres- sions. The control string may include the actual output string in addition to control values. For example, the statement int num1, num2, num3; num1 = 34; num2 = 9; num3 = num1 + num2; formatter.format(%3d + %3d = %5d, num1, num2, num3); will output 34 + 9 = 43 Figure 6.12 shows how the control values are matched left to right against the arguments. The figure also illustrates how the noncontrol values (such as + and = symbols) are output to the destination. We can change the default left-to-right argument matching by including the argument index in the control string. The arguments can be indexed as 1$, 2$, and so forth. For example, the output of formatter.format(%3$3d is the sum of %2$3d and %1$3d, num1, num2, num3); will be 43 is the sum of 9 and 34 328 Chapter 6 Repetition Statements control string Figure 6.12 The control values are matched left to right. 3 formatter.format (%3d + %3d = %5d, num1, num2, num3); 4 + 9 = 4 3 wu23399_ch06.qxd 12/14/06 17:53 Page 328
  • 356. To format real numbers, we include the number of decimal places along with the field width in the following format: %field width . decimal places f The control letter f designates formatting a floating-point number. Here’s an exam- ple to format 345.9867 using a field of width 15 and two decimal places: formatter.format(%15.3f, 345.9867); To format a string, we use the control letter s. Here’s an example: String name = John; formatter.format(Hello, %s. Nice to meet you., name); The output will be Hello, John. Nice to meet you. We can also use the format method to format the date information. We use the control letter t for formatting an instance of GregorianCalendar or Date. The control letter t must be followed by another control letter that designates the formatting of the components of the date information, such as month, day, or year. For example, if we write GregorianCalendar day = new GregorianCalendar(1776, 6, 4); formatter.format(%1$tB %1$te, %1$tY, day); the output will be July 4, 1776 The date control letter B designates the full month name, e designates the day in two digits, and Y designates the year in four digits. For other data control letters, please consult the documentation. Notice that there is only one output argument, and it is referred to as 1$ three times in the control string. The use of the Formatter class gives us the most control over the formatting, but for common output formatting, we can do it by using the format method of System.out or the String class instead. (In this section, we presented only a subset of common formatting.) For example, the following code System.out.format(%5s is %3d years old, Bill, 20); is equivalent to Formatter formatter = new Formatter(System.out); formatter.format(%5s is %3d years old, Bill, 20); 6.7 Formatting Output 329 wu23399_ch06.qxd 12/14/06 17:53 Page 329
  • 357. (Note: For those who are familiar with C or C++, there’s a method named printf de- fined for System.out that works exactly the same as the format method. However, Java’s printf is similar but not identical to the one in C or C++.) Instead of printing out, it is possible to create a formatted string and assign it to a variable with the format method of the String class. Here’s an example: String outputStr = String.format(%3d + %3d = %5d, num1, num2, num3); We close the section with a program that produces the carpet price table with proper alignment for the range of values used in producing the table in Figure 6.10. Running this program will produce the table shown in Figure 6.13. 330 Chapter 6 Repetition Statements Figure 6.13 Carpet price table of Figure 6.11 with proper alignment. /* Chapter 6 Sample Program: Sample formatting statements File: Ch6CarpetPriceTableWithFormat.java */ class Ch6CarpetPriceTableWithFormat { public static void main (String[] args) { int price; //print out the column labels System.out.print( ); //put three blank spaces first for (int colLabel = 5; colLabel =25; colLabel += 5) { System.out.format(%8d, colLabel); } wu23399_ch06.qxd 12/14/06 17:53 Page 330
  • 358. 6.8 Loan Tables 331 System.out.println(); System.out.println(); //print out rows of prices for (int width = 5; width = 14; width++) { System.out.format(%3d, width); for (int length = 5; length = 25; length += 5) { price = width * length * 15; System.out.format(%8d, price); } //finished one row; now move on to the next row System.out.println(); } System.out.println(); System.out.println(); } } 1. Determine the output of the following code. System.out.format(%3d + %3d = %3d, 1, 2, 3); System.out.format(%tY, new Date()); System.out.format(%2$s,%1$s, John, Smith); 2. What’s wrong with the following code? Formatter f = new Formatter( ); f.format(%8.3f, 232.563); 6.8 Loan Tables The LoanCalculator program computed the monthly and total payments for a given loan amount, annual interest rate, and loan period. To see the monthly payment for the same loan amount and loan period but with a different interest rate, we need to repeat the calculation, entering the three values again. To illustrate the use of the concepts introduced in this chapter, let’s design a program that generates a loan table (similar to the carpet price table) for a given loan amount so we can compare different monthly payments easily and quickly. The columns of the table are the loan periods in number of years (5, 10, 15, 20, 25, 30), and the rows are interest rates ranging from 6 to 10 percent in increments of 0.25. wu23399_ch06.qxd 12/14/06 17:53 Page 331
  • 359. In this section, we provide a discussion of the relevant methods only. Let’s begin with a design of the topmost start method of the top-level controller class. The start method can be expressed as tell the user what the program does; prompt the user Do you want to generate a loan table?; while (the user says YES) { input the loan amount; generate the loan table; prompt the user Do you want another loan table?; } The start method is expressed in pseudocode. Pseudocode is an informal language we often use to express an algorithm. Pseudocode is useful in expressing an algo- rithm without being tied down to the rigid syntactic rules of a programming lan- guage. We can express a simple algorithm in the actual programming language statements, but for a more complex algorithm, especially those involving nonse- quential control flow logic, pseudocode is very helpful in expressing the algorithm concisely and clearly. Whenever appropriate, we will use pseudocode to express more complex algorithms in the remainder of the book. Translating the pseudocode into Java code will result in private static enum Response {YES, NO} public void start( ) { Response response; describeProgram(); response = prompt(Generate a loan table?); while (response == Response.YES) { loanAmount = getLoanAmount(); //get input generateLoanTable(loanAmount); //generate table response = prompt(Generate another loan table?); } } private Response prompt(String question) { String input; Response response = Response.NO; System.out.print(question + (Yes - y; No - n): ); input = scanner.next(); 332 Chapter 6 Repetition Statements pseudocode scanner is created in a constructor wu23399_ch06.qxd 12/14/06 17:53 Page 332
  • 360. if (input.equals(Y) || input.equals(y)) { response = Response.YES; } return response; } Notice how the actual start method is almost as easy to read as the pseudocode. By using objects and well-designed (sub)methods, we can express methods that are as easy to read as pseudocode. The describeProgram method tells the user what the program does if the user requests it. The getLoanAmount method gets the loan amount from the user. The method will allow the user to enter the loan amount between 100.0 and 500000.0. The generateLoanTable method generates the loan table, which we explain in detail next. We use a nested loop to generate the table. Both the inner and outer loops are count-controlled loops. The loop for columns (years) will range from 5 to 30 with an increment of 5 and the loop for rows (rates) will range from 6.0 to 10.0 with an increment of 0.25. So the nested loop can be written as follows: private static final int BEGIN_YEAR = 5; private static final int END_YEAR = 30; private static final int YEAR_INCR = 5; private static final double BEGIN_RATE = 6.0; private static final double END_RATE = 10.0; private static final double RATE_INCR = 0.25; ... for (double rate = BEGIN_RATE; rate = END_RATE; rate += RATE_INCR){ for (int year = BEGIN_YEAR; year = END_YEAR; year += YEAR_INCR){ ... //compute and display the monthly loan payment //for a given year and rate } } Notice the outer loop is using double as the loop counter, something we dis- couraged in Section 6.2. In this particular case, with the increment value of 0.25, there will be no problem because this value can be represented precisely in com- puter memory. Moreover, the terminating condition rate = END_RATE guarantees that the loop will terminate eventually if we keep adding RATE_INCR to rate. To compute the monthly loan payment, we simply reuse the Loan class we defined in Chapter 4 as follows: double amount = ... ; double rate = ... ; int period = ... ; 6.8 Loan Tables 333 wu23399_ch06.qxd 12/14/06 17:53 Page 333
  • 361. Loan loan = new Loan( ); double monthlyPayment = loan.getMonthlyPayment(amount, rate, period); This is the power of object-oriented programming. Because a single well- defined task of loan computation and nothing else is coded in the Loan class, we are able to reuse it here easily. What would happen had we not designed the Loan class? It was certainly possible for us to complete the Chapter 4 sample development pro- gram with one service class that handles everything: input, output, and computation tasks. The chance of reusing such a class, however, is very low. Just as we do not expect to buy a textbook that teaches all five subject matters of single-variable cal- culus, introduction to economics, organic chemistry, introduction to programming, and western civilization, we do not want a service class that is overloaded with many different types of tasks. We do not want one class that does everything. Rather, we want many classes, with each class doing one task effectively and effi- ciently. This will allow us to mix and match the classes easily. Finally, the output values can be formatted by using the technique introduced in Section 6.7. Overall design is now complete. It is left as an exercise (Exercise 17) to implement the loan table calculation program. 6.9 Estimating the Execution Time We promised at the end of Section 6.1 to compare the two versions of gcd methods experimentally. Detailed analysis of algorithms is beyond the scope of this book (we provide a little bit of analytical comparisons of sorting algorithms in this book), but experimental analysis is within our realm. We can compare the performance of different methods by actually running them and clocking their execution times. Here’s the basic idea: Start the clock (stopwatch) Run the method Stop the clock Report the elapsed time There is no clock or stopwatch standard class, but we can time the execution by using the Date class from the java.util package. Before we call the method we want to time, we record the start time by creating a Date object. After the method is completed, we record the end time by creating a second Date object. Calling the getTime method of the Date class returns the number of milliseconds (1 ms 1/1000 s) since January 1, 1970 00:00:00 Greenwich Mean Time. So by subtracting the start time from the end time, we can get the elapsed time in milliseconds. Here’s the general idea: Date startTime = new Date(); //the method call comes here 334 Chapter 6 Repetition Statements wu23399_ch06.qxd 12/14/06 17:53 Page 334
  • 362. Date endTime = new Date(); long elapsedTimeInMilliseconds = endTime.getTime() - startTime.getTime(); Now let’s write a short program to time the performance of gcd and gcd_bruteforce. The program includes many of the techniques discussed in this chapter. Here’s the program (we do not repeat the method bodies of gcd and gcd_bruteforce here): 6.9 Estimating the Execution Time 335 /* Chapter 6 Sample Program: Time the performance of gcd methods File: Ch6TimeGcd.java */ import java.util.*; class Ch6TimeGcd { private static enum ComputationType {BRUTE_FORCE, EUCLID} private Scanner scanner; public static void main(String[] args) { Ch6TimeGcd tester = new Ch6TimeGcd( ); tester.start(); System.exit(0); } public Ch6TimeGcd() { scanner = new Scanner(System.in); } public void start( ) { long bruteForceTime, euclidTime; int m, n; while (isContinue()) { m = getPositiveInteger( ); n = getPositiveInteger( ); //Time the brute force method bruteForceTime = timeMethod(m, n, ComputationType.BRUTE_FORCE); //Time the Euclidean method euclidTime = timeMethod(m, n, ComputationType.EUCLID); wu23399_ch06.qxd 12/14/06 17:53 Page 335
  • 363. 336 Chapter 6 Repetition Statements System.out.println(M: + m); System.out.println(N: + n); System.out.println(Brute Force Time: + bruteForceTime); System.out.println(Euclidean Time: + euclidTime + n); } } private long timeMethod(int m, int n, ComputationType type) { Date startTime, endTime; startTime = new Date(); if (type == ComputationType.BRUTE_FORCE) { gcd_bruteforce(m, n); } else { gcd(m, n); } endTime = new Date(); return (endTime.getTime() - startTime.getTime()); } private int getPositiveInteger( ) { int input; while (true) { System.out.print(Enter positive integer (0 is okay):); input = scanner.nextInt(); if (input = 0) break; System.out.println(Input must be 0 or more); } return input; } private boolean isContinue( ) { String input; boolean response = false; System.out.print(Run test? ); input = scanner.next(); if (input.equals(Y) || input.equals(y)) { response = true; } wu23399_ch06.qxd 12/14/06 17:53 Page 336
  • 364. return response; } private int gcd_bruteforce(int m, int n) { . . . } private int gcd(int m, int n) { . . . } } 6.9 Estimating the Execution Time 337 Here’s a sample interaction: Run test? y Enter positive integer (0 is okay):4567820 Enter positive integer (0 is okay):2147483640 M: 4567820 N: 2147483640 Brute Force Time: 94 Euclidean Time: 0 Run test? y Enter positive integer (0 is okay):1457689098 Enter positive integer (0 is okay):2147483640 M: 1457689098 N: 2147483640 Brute Force Time: 31953 Euclidean Time: 0 Run test? n The value of 0 for Euclidean time does not imply that it took no time to com- pute the result. It means that the time it took was so miniscule, we weren’t able to detect it by the technique we used. Notice that, for the brute-force approach, the dif- ference in the running times between the small and large values for M is substantial, while the difference for the Euclidean approach is not discernible. Detailed analysis will actually tell us that the running time for the brute-force approach is linearly proportional to the input size M, while the Euclidean approach is logarithmically proportional to the input size M. So, for the second comparison in the sample run, there will be 1,457,689,098 divisions performed (actually twice this number because we are executing m % i == 0 n % i == 0) in gcd_bruteforce, but only log 1,457,689,098 9 divisions. See how superior the Euclidean approach is? wu23399_ch06.qxd 12/14/06 17:53 Page 337
  • 365. Keep in mind that the value we get for the elapsed time is a rough estimate. For one thing, the values we get for the elapsed time differ dramatically according to which CPU we run the program on and whether other processes are running at the same time (e.g., if a garbage collection routine kicks in while a method is running, then the runtime estimate can be way off). Also, the granularity is very coarse when timed from a high-level language such as Java. For example, it is not possible to dis- tinguish between the program that runs in 5 ms and the program that runs in 6 ms. Although the value is a rough estimate, it still give us useful information such as the rate of increase in execution time as we increase the size of input values. 338 Chapter 6 Repetition Statements To estimate the running time of a loop statement: 1. Record the start time by creating a Date object,say, startTime, before the loop statement. 2. Record the end time by creating another Date object,say,endTime, after the loop statement. 3. Elapsed time (in milliseconds) is computed as follows: elapsedTime = endTime.getTime() – startTime.getTime(); 6.10 Recursive Methods (Optional) In addition to the three repetition control statements we introduced in this chapter, there is a fourth way to control the repetition flow of a program by using recursive methods. A recursive method is a method that contains a statement (or statements) that makes a call to itself. We explain recursive methods briefly in this section. Realistic examples of recursive methods will be given in Chapter 15. So far, we have seen only methods that call other methods, something like this: methodOne(...) { ... methodTwo(...); //methodOne called methodTwo ... } methodTwo(...) { ... } A recursive method calls itself, and it looks something like this: methodOne(...) { ... recursive method wu23399_ch06.qxd 12/14/06 17:53 Page 338
  • 366. methodOne (...); //calls the method itself ... } At first glance, it seems as if a recursive call will never end since the call is made to the same method. Indeed, if you do not follow the rules, you could end up with infinite recursive calls. In this section we explain how to write recursive methods correctly. Suppose we want to compute the factorial of N. The factorial of N is the prod- uct of the first N positive integers, denoted mathematically as N! = N * (N-1) * (N-2) * ... * 2 * 1 We will write a recursive method to compute the factorial of N. Mathemati- cally, we can define the factorial of N recursively as 6.10 Recursive Methods (Optional) 339 factorial(N)= 1 if N = 1 N * factorial (N-1) otherwise The definition states that if N is 1, then the function factorial(N) has the value 1. Otherwise, the function factorial(N) is the product of N and factorial(N –1). For example, the function factorial(4) is evaluated as follows: The recursive factorial method parallels the preceding mathematical defini- tion. The method is defined thus: //Assume N is greater than 0 public int factorial(int N) { if (N == 1) return 1; else return N * factorial(N-1); } factorial(4) 4 * factorial(3) 24 6 2 1 3 * factorial(2) 2 * factorial(1) 1 Recursive case: recursion continues with another recursive call. Test to stop or continue. End case: recursion stops. wu23399_ch06.qxd 12/14/06 17:53 Page 339
  • 367. The diagram in Figure 6.14 illustrates the sequence of calls for the recursive factorial method. Recursive methods will contain three necessary components. 340 Chapter 6 Repetition Statements The three necessary components in a recursive method are 1. A test to stop or continue the recursion. 2. An end case that terminates the recursion. 3. A recursive call(s) that continues the recursion. To ensure that the recursion will stop eventually, we must pass arguments different from the incoming parameters. In the factorial method, the incoming parameter was N, while the argument passed in the recursive call was N1. This difference of 1 be- tween the incoming parameter and the argument will eventually make the argument in a recursive call be 1, and the recursion will stop. Let’s implement two more mathematical functions using recursion. The next method computes the sum of the first N positive integers 1, 2, . . ., N. Notice how this method includes the three necessary components of a recursive method. public int sum ( int N ) { //assume N = 1 if (N == 1) return 1; else return N + sum( N-1 ); } The last method computes the exponentiation AN , where A is a real number and N is a positive integer. This time, we have to pass two arguments—A and N. The value of A will not change in the calls, but the value of N is decremented after each recursive call. public double exponent ( double A, int N ) { if (N == 1) return A; else return A * exponent( A, N-1 ); } So far we used only mathematical functions to illustrate recursive methods, but recursion is not limited to mathematical functions. Let’s look at one example. We know the length method of the String class returns the number characters in a given string. Let’s write a recursive method that does the same thing. Here’s how we think recursively. The total number of characters in a string is 1 plus the number of characters in the substring from the second position to the end of the string. If the wu23399_ch06.qxd 12/14/06 17:53 Page 340
  • 368. 6.10 Recursive Methods (Optional) 341 int factorial(int N){ if (N==1) return 1; else return N * factorial(N–1); } N4 24 int factorial(int N){ if (N==1) return 1; else return N * factorial(N–1); } N3 6 int factorial(int N){ if (N==1) return 1; else return N * factorial(N–1); } N2 2 int factorial(int N){ if (N==1) return 1; else return N * factorial(N–1); } N1 1 Figure 6.14 The sequence of calls for the recursive factorial method. wu23399_ch06.qxd 12/14/06 17:53 Page 341
  • 369. string has no characters, then the length is zero. Puting this idea into an actual method, we have public int length(String str) { if (str.equals()) { //str has no characters return 0; } else { return 1 + length(str.substring(1)); } } We will present more examples of recursive methods that implement nonnu- merical operations in Chapter 15. We used factorial, sum, exponentiation, and length as examples to introduce some of the basic concepts of recursion, but we should never actually write these methods using recursion. The methods can be written more efficiently in an itera- tive (i.e., nonrecursive) manner using a simple for loop. In practice, we use recur- sion if certain conditions are met. 342 Chapter 6 Repetition Statements Index of the second position is 1. Use recursion if 1. A recursive solution is natural and easy to understand. 2. A recursive solution does not result in excessive duplicate computation. 3. The equivalent iterative solution is too complex. As a final review of the topic, we conclude this section with the recursive version of the Euclidean gcd method. Remember the logic behind the Euclidean gcd method is a sequence of reducing the problem, for example, gcd(48, 16) = gcd(16, 12) = gcd(12, 4) = 4. Here’s how we can express this thinking recursively: public int gcd_recursive(int m, int n) { int result; if (m == 0) { //test result = n; //end case } else { result = gcd_recursive(n % m, m); //recursive case } return result; } wu23399_ch06.qxd 12/14/06 17:53 Page 342
  • 370. 6.11 Sample Development 343 Sample Development 6.11 Sample Development Hi-Lo Game In this section we will develop a program that plays a Hi-Lo game.This program illustrates the use of repetition control,the random number generator,and the testing strategy.The objective of the game is to guess a secret number between 1 and 100.The program will respond with HI if the guess is higher than the secret number and LO if the guess is lower than the secret number.The maximum number of guesses allowed is six.If we allow up to seven,one can always guess the secret number.Do you know why? Problem Statement Write an application that will play Hi-Lo games with the user.The objective of the game is for the user to guess the computer-generated secret number in the least number of tries. The secret number is an integer between 1 and 100, inclusive. When the user makes a guess, the program replies with HI or LO depending on whethertheguessishigherorlowerthanthesecretnumber.Themaximumnum- ber of tries allowed for each game is six.The user can play as many games as she wants. Overall Plan We will begin with our overall plan for the development. Let’s identify the major tasks of the program.The first task is to generate a secret number every time the game is played, and the second task is to play the game itself.We also need to add a loop to repeat these two tasks every time the user wants to play the Hi-Lo game.We can express this program logic in pseudocode as follows: do { Task 1: generate a secret number; Task 2: play one game; } while ( the user wants to play ); Let’s look at the two tasks and determine objects that will be responsible for han- dling the tasks.For the first task,we will use the random method of the Math class.We will examine this method in detail later to determine whether it is the one we can use in the program. If this method does not meet our needs, then we will explore further and most likely will have to derive our own random number generator. For the second task of playing the game itself, we use objects that handle I/O and the logic of repeatedly asking for the next guess until the game is over.For input and out- put,we use a Scanner and System.out. We will define a class to handle the logic of play- ing the game. This class will control the other two classes. We will name this class Ch6HiLo, and it will be an instantiable main class. program tasks wu23399_ch06.qxd 12/14/06 17:53 Page 343
  • 371. 344 Chapter 6 Repetition Statements 6.11 Sample Development—continued Here’s our working design document: Design Document: Ch6HiLo Class Purpose Ch6HiLo The top-level control object handles the logic of playing games and manages other objects.This is the instantiable main class. Scanner This standard class is for inputting user guesses. PrintStream This standard class is for displaying hints and other (System.out) messages. Figure 6.15 is the program diagram for this program. A keen observer may have noticed that the Ch6HiLo class is handling both types of tasks:handling of user interface and con- trolling the logic of game playing. We will revisit this design in the GUI chapter and provide an alternative.The one-class design we adopt here may not be an ideal design, but may be acceptable for a simplistic game such as this one.The design also provides us with a meaningful comparison when we present an alternative design in the GUI chapter. We will implement this program using the following four major steps: 1. Start with a skeleton Ch6HiLo class. 2. Add code to the Ch6HiLo class to play a game using a dummy secret number. 3. Add code to the Ch6HiLo class to generate a random number. 4. Finalize the code by removing temporary statements and tying up loose ends. Step 1 Development: Program Skeleton The structure of the HiLoMain class is the same as other main classes.All we need to do is to declare, create, and start a HiLo object. Instead of forcing the user to play at least one program classes Figure 6.15 The program diagram for the HiLo program. Ch6HiLo PrintStream (System.out) Scanner develop- ment steps step 1 design wu23399_ch06.qxd 12/14/06 17:53 Page 344
  • 372. game,we will implement the program so the user has an option of not playing a game at all.In pseudocode we can express this logic as follows: describe the game rules; prompt the user to play a game or not; while ( answer is yes ) { generate the secret number; play one game; prompt the user to play another game or not; } Notice that we use a while loop here, so the user can quit the program without playing a game. If we use a do–while loop instead, then the user must play at least one game before stopping the program.We opt to use the while loop because the user may not want to play the game at all after reading the game rules. We use a private method describeRules to display the game rules.Another private method named prompt gets a yes/no reply from the user.We call this method to ask if the user wants to play a game.To generate a secret number,we have the third private method generateSecretNumber. Lastly, we define the fourth private method playGame to play one game.We declare these four methods private because these methods are for inter- nal use. As always, we will use the constructor to perform necessary object creation and initialization. Our working design document for the HiLo class is as follows: 6.11 Sample Development 345 Design Document:The Ch6HiLo Class Method Visibility Purpose constructor public Creates and initializes the objects used by a HiLo object. start public Starts the Hi-Lo game playing.The user has an option of playing a game or not. describeRules private Displays the game rules in System.out. generateSecretNumber private Generates a secret number for the next Hi-Lo game. playGame private Plays one Hi-Lo game. prompt private Prompts the user for a yes/no reply. wu23399_ch06.qxd 12/14/06 17:53 Page 345
  • 373. 6.11 Sample Development—continued 346 Chapter 6 Repetition Statements For the skeleton program, we include temporary output statements in the private methods to verify that they are called correctly in the right order. Here’s the skeleton Ch6HiLo class: step 1 code import java.util.*; /* Chapter 6 Sample Development: Hi-Lo Game (Step 1) The instantiable main class of the program. */ class Ch6HiLo { private static enum Response {YES, NO} private Scanner scanner; //Main Method public static void main (String[] args) { Ch6HiLo hiLo = new Ch6HiLo( ); hiLo.start(); } public Ch6HiLo( ) { scanner = new Scanner(System.in); } public void start( ) { Response answer; describeRules(); answer = prompt(Do you want to play a Hi-Lo game?); while (answer == Response.YES) { generateSecretNumber( ); playGame(); answer = prompt(Do you want to play another Hi-Lo game?); } System.out.println(Thank you for playing Hi-Lo.); } Constructor start main wu23399_ch06.qxd 12/14/06 17:54 Page 346
  • 374. private void describeRules( ) { System.out.println(Inside describeRules); //TEMP } private void generateSecretNumber( ) { System.out.println(Inside generateSecretNumber); //TEMP } private void playGame( ) { System.out.println(Inside playGame); //TEMP } private Response prompt(String question) { String input; Response response = Response.NO; System.out.print(question + (Yes - y; No - n): ); input = scanner.next(); if (input.equals(Y) || input.equals(y)) { response = Response.YES; } return response; } 6.11 Sample Development 347 describeRules generateSecretNumber playGame prompt We execute the skeleton Ch6HiLo class to verify that the class is coded correctly. To verify the correct execution of step 1,we attempt to play the game 1. Zero times 2. One time 3. One or more times For the first run, we select No to the prompt Do you want to play a Hi-Lo game? and make sure the program stops without playing a game. For the second run, we select Yes to the first prompt and verify that the messages Inside generateSecretNumber and Inside playGame are shown in the console window. We select No to the prompt Do you want to play another Hi-Lo game? and make sure the program stops.For the third run, we make sure we can play more than one game. After we verify all the scenarios work correctly,we proceed to the next step. step 1 test wu23399_ch06.qxd 12/14/06 17:54 Page 347
  • 375. 6.11 Sample Development—continued Step 2 Development: Play a Game with a Dummy Secret Number In the second development step, we add a routine that plays a Hi-Lo game. Let’s begin with the control flow of the playGame method. There are two cases to end a Hi-Lo game: The user either guesses the number in less than six tries or uses up all six tries without guessing the number. So we need a counter to keep track of the number of guesses made.Let’s call this counter guessCount. We stop the game when guessCount becomes larger than 6 or the user’s guess is equal to the secret number. At the end of the game, we output an appropriate message. Expressing this in pseudocode, we have //Method: playGame set guessCount to 0; do { get next guess; increment guessCount; if (guess secretNumber) { print the hint LO; } else if (guess secretNumber) { print the hint HI; } } while (guessCount number of guesses allowed guess != secretNumber ); if (guess == secretNumber) { print the winning message; } else { print the losing message; } All variables used in this method will be local except secretNumber, which will be an in- stance variable. The value for secretNumber is set inside the generateSecretNumber method. To support a better user interface,we will include an input error handling that allows the user to enter only values between 1 and 100. We will do this input-error-checking rou- tine in a new private method getNextGuess because we do want to keep the playGame method clean and simple. If we included the code for input error handling directly inside 348 Chapter 6 Repetition Statements step 2 design wu23399_ch06.qxd 12/14/06 17:54 Page 348
  • 376. the playGame method, the method would become too cluttered and lose the overall clar- ity of what the method is doing. Pseudocode for the getNextGuess method is //Method: getNextGuess while ( true ) { get input value; if (valid input) return input value; print error message; } The working design document of the class now includes this new private method: 6.11 Sample Development 349 step 2 code Design Document: The Ch6HiLo Class Method Visibility Purpose ... ... ... getNextGuess private Returns the next guess from the user.Only accepts a guess between 1 and 100. Prints an appropriate error message when an invalid guess is entered. In the step 2 coding, we need to implement three methods. In addition to the playGame and getNextGuess methods, we need to define a temporary gen- erateSecretNumber method so we can test the playGame method. The temporary generateSecretNumber method assigns a dummy secret number to the instance vari- able secretNumber. The temporary method is coded as follows: private void generateSecretNumber( ) { secretNumber = 45; //TEMP } Any number will do; we simply picked the number 45. Knowing that the secret number is 45, we will be able to test whether the playGame method is implemented correctly. We implement the playGame method thus: private void playGame( ) { int guessCount = 0; int guess; do { //get the next guess guess = getNextGuess(); guessCount++; getNextGuess is a new private method. wu23399_ch06.qxd 12/14/06 17:54 Page 349
  • 377. 6.11 Sample Development—continued //check the guess if (guess secretNumber) { System.out.println Your guess is LO); } else if (guess secretNumber) { System.out.println Your guess is HI); } } while ( guessCount MAX_GUESS_ALLOWED guess != secretNumber ); //output appropriate message if ( guess == secretNumber ) { System.out.println You guessed it in + guessCount + tries.); } else { System.out.println You lost. Secret No. was + secretNumber); } } The getNextGuess method will accept an integer between 1 and 100. The method uses a while loop to accomplish this: private int getNextGuess( ) { int input; while (true) { System.out.print(Next Guess: ); input = scanner.nextInt(); if (LOWER_BOUND = input input = UPPER_BOUND) { return input; } //invalid input; print error message System.out.println(Invalid Input: + Must be between + LOWER_BOUND + and + UPPER_BOUND); } } 350 Chapter 6 Repetition Statements Repeat the loop if the number of tries is not used up and the correct guess is not made. This class constant is set to 6. wu23399_ch06.qxd 12/14/06 17:54 Page 350
  • 378. The necessary constant and instance variable are declared in the data member sec- tion of the HiLo class as follows: //--------------------------------- // Data Members //--------------------------------- private final int MAX_GUESS_ALLOWED = 6; private final int LOWER_BOUND = 1; private final int UPPER_BOUND = 100; private int secretNumber; We need to test two methods in this step. To verify the getNextGuess method,we input both invalid and valid guesses. We verify the method by running the following tests: 1. Enter a number less than 1. 2. Enter a number greater than 100. 3. Enter a number between 2 and 99. 4. Enter 1. 5. Enter 100. The first two test cases are called error cases, the third is called the normal case, and the last two are called end cases. One of the common errors beginners make is to create a loop statement that does not process the end cases correctly. When our code handles all three types of cases correctly, we will proceed to test the playGame method. To verify the playGame method, we need to perform a more elaborate testing. Knowing that the dummy secret number is 45, we verify the playGame method by run- ning the following tests: 1. Enter a number less than 45 and check that the correct hint LO is displayed. 2. Enter a number greater than 45 and check that the correct hint HI is displayed. 3. Enter the correct guess,and check that the game terminates after displaying the appropriate message. 4. Enter six wrong guesses,and check that the game terminates after displaying the appropriate message. When all four tests are successfully completed, we proceed to the next step. Step 3 Development: Generate a Random Number In step 3,we add a routine that generates a random number between 1 and 100. As explained in Chapter 3, we can use the method random from the Math package. Since 6.11 Sample Development 351 step 2 test test cases step 3 design wu23399_ch06.qxd 12/14/06 17:54 Page 351
  • 379. 6.11 Sample Development—continued the range is between 1 and 100, we can simplify the formula as secretNumber = X 100 1 where 0.0 ≤ X 1.0. The generateSecretNumber method is defined thus: private void generateSecretNumber( ) { double X = Math.random(); secretNumber = (int) Math.floor( X * 100 ) + 1; System.out.println(Secret Number: + secretNumber); // TEMP } The method includes a temporary statement to output the secret number so we can stop the game anytime we want by entering the correct guess. To verify that the method generates correct random numbers,we will write a sepa- rate test program. If we don’t use such a test program and instead include the method immediately in the Ch6HiLo class, we have to play the game, say, 100 times to verify that the first 100 generated numbers are valid. The test program generates N random num- bers and stops whenever an invalid number is generated. We will set N to 1000. Here’s the test program: class TestRandom { public static void main (String[] args) { int N = 1000, count = 0, number; double X; do { count++; X = Math.random(); number = (int) Math.floor( X * 100 ) + 1; } while ( count N 1 = number number = 100 ); if ( number 1 || number 100 ) { System.out.println(Error: + number); } else { System.out.println(Okay); } } } 352 Chapter 6 Repetition Statements step 3 code step 3 test TestRan- dom class for testing wu23399_ch06.qxd 12/14/06 17:54 Page 352
  • 380. Keep in mind that successfully generating 1000 valid random numbers does not guarantee that the 1001st number is also valid. We did not offer any formal mathematical proof that the routine for the random number generator works correctly. What we are doing here is making an assumption that no user wants to play more than 1000 Hi-Lo games in one session, which we believe is a practical and reasonable assumption. After the TestRandom class is executed correctly, we make the necessary changes to the Ch6HiLo class and run it. When we verify that the program runs as expected, we proceed to the final step. Step 4 Development: Finalize We finalize the program in the last step. We will perform a critical review of the program, looking for any unfinished method, inconsistency, or error in the methods; unclear or missing comments; and so forth. We should also not forget to keep an eye on any im- provement we can make to the existing code. We still have a temporary code inside the describeRules method, so we will complete the method by adding code to describe the game rules. This method is left as Exercise 18. There are still temporary output statements that we used for verification purposes. We can either delete them from the program or comment them out. We will leave them in the program by commenting them out so when the time comes for us to modify, debug,or update the program,we do not have to reenter them. Summary 353 program review • A repetition control statement is used to repeatedly execute a block of code until a certain condition is met. • Three repetition control statements are while, do–while, and for. • The count-controlled loop executes the loop body for a fixed number of times. • The sentinel-controlled loop executes the loop body until any one of the designated values called a sentinel is encountered. • Count-controlled loops can be implemented most naturally with the for statements. • Sentinel-controlled loops can be implemented most naturally with the while or do–while statements. • The while statement is called a pretest loop, and the do–while statement is called a posttest loop. The for statement is also a pretest loop. • Reading a value before the loop statement is called a priming read. • Off-by-1 error and infinite loop are two common mistakes in writing a loop control. S u m m a r y wu23399_ch06.qxd 12/14/06 17:54 Page 353
  • 381. • The loop-and-a-half repetition control is the most general way of writing a loop. The break statement is used within the loop body to exit the loop when a certain condition is met. • The nested for statement is used very often because it is ideally suited to process tabular data. • Output values can be formatted by using the Formatter class. • Execution time can be estimated by using the Dafe class. 354 Chapter 6 Repetition Statements repetition statements while statements do–while statements for statements off-by-1 error infinite loop priming read nested for statements pseudocode loop-and-a-half control one-entry-one-exit control count-controlled loops sentinel-controlled loops pretest and posttest loops formatting output values recursive methods (optional) K e y C o n c e p t s 1. Identify all the errors in the following repetition statements. Some errors are syntactical while others are logical (e.g., infinite loops). a. for (int i = 10; i 0; i++) { x = y; a = b; } b. int sum = 0; Scanner scanner = new Scanner(System.in); do { num = scanner.nextInt(); sum += num; } until (sum 10000); c. while (x 1 x 10) { a = b; } d. while (a == b) ; { a = b; x = y; } E x e r c i s e s wu23399_ch06.qxd 12/14/06 17:54 Page 354
  • 382. e. for (int i = 1.0; i = 2.0; i += 0.1) { x = y; a = b; } 2. Write for, do–while, and while statements to compute the following sums and products. a. 1 2 3 . . . 100 b. 5 10 15 . . . 50 c. 1 3 7 15 31 . . . (220 1) d. 1 1 2 1 3 1 4 . . . 1 1 5 e. 1 2 3 . . . 20 f. 1 2 4 8 . . . 220 3. What will be the value of sum after each of the following nested loops is executed? a. sum = 0; for (int i = 0; i = 10; i++) for (int j = 0; j = 10; j++) sum += i ; b. sum = 0; j = 0; do { j++; for (int i = 5; i j; i--) sum = sum + (i+j); } while (j 11); c. sum = 0; i = 0; while (i 5) { j = 5; while (i != j) { sum += j; j--; } i++; } d. sum = 0; for (int i = 0; i = 10; i++) for (int j = 10; j 2*i; j--) sum = sum + (j - i); Exercises 355 wu23399_ch06.qxd 12/14/06 17:54 Page 355
  • 383. 4. Determine the output from the following code without actually executing it. System.out.format(%4d, 234); System.out.format(%5d, 234); System.out.format(%s, n); System.out.format($%6.2f, 23.456); System.out.format(%s, n); System.out.format(%1$3d+%1$3d=%2$5d, 5, (5+5)); 5. Rewrite the following nested for statements, using nested do–while and while statements. a. sum = 0; number = 0; for (int i = 0; i = 10; i++) for (int j = 10; j = i; j--) { number++; sum = sum + (j - i); } b. product = 1; number = 0; for (int i = 1; i 5; i++) for (int j = 1; j 5; j++) { number++; product *= number; } 6. You can compute sin x and cos x by using the following power series: sin x x 3 x3 ! 5 x5 ! 7 x7 ! . . . cos x 1 2 x2 ! 4 x4 ! 6 x6 ! . . . Write a program that evaluates sin x and cos x by using the power series. Use the double data type, and increase the number of terms in the series until the overflow occurs. You can check if the overflow occurs by comparing the value against Double. POSITIVE_INFINITY. Compare the results you obtain to the values returned by the sin and cos methods of the Math class. 7. Write an application to print out the numbers 10 through 49 in the following manner: 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 356 Chapter 6 Repetition Statements wu23399_ch06.qxd 12/14/06 17:54 Page 356
  • 384. How would you do it? Here is an example of poorly written code: for (int i = 10; i 50; i++) { switch (i) { case 19: case 29: case 39: System.out.println( + i); //move to the break; //next line default: System.out.print( + i); } } This code is not good because it works only for printing 10 through 49. Try to develop the code so that it can be extended easily to handle any range of values. You can do this coding in two ways: with a nested for statement or with modulo arithmetic. (If you divide a number by 10 and the remainder is 9, then the number is 9, 19, 29, or 39, and so forth.) 8. A prime number is an integer greater than 1 and divisible by only itself and 1. The first seven prime numbers are 2, 3, 5, 7, 11, 13, and 17. Write a method that returns true if its parameter is a prime number. 9. There are 25 primes between 2 and 100, and there are 1229 primes between 2 and 10,000. Write a program that inputs a positive integer N 2 and displays the number of primes between 2 and N (inclusive). Use the timing technique explained in Section 6.9 to show the amount of time it took to compute the result. 10. Instead of actually computing the number of primes between 2 and N, we can get an estimate by using the Prime Number Theorem, which states that prime(N ) ln N (N) where prime(N) is the number of primes between 2 and N (inclusive). The function ln is the natural logarithm. Extend the program for Exercise 9 by printing the estimate along with the actual number. You should notice the pattern that the estimate approaches the actual number as the value of N gets larger. 11. A perfect number is a positive integer that is equal to the sum of its proper divisors. A proper divisor is a positive integer other than the number itself that divides the number evenly (i.e., no remainder). For example, 6 is a perfect number because the sum of its proper divisors 1, 2, and 3 is equal to 6. Eight is not a perfect number because 1 2 4 8 . Write an application that accepts a positive integer and determines whether the number is perfect. Also, display all proper divisors of the number. Try a number between 20 and 30 and another number between 490 and 500. 12. Write an application that lists all perfect numbers between 6 and N, an upper limit entered by the user. After you verify the program with a small number Exercises 357 wu23399_ch06.qxd 12/14/06 17:54 Page 357
  • 385. for N, gradually increase the value for N and see how long the program takes to generate the perfect numbers. Since there are only a few perfect numbers, you might want to display the numbers that are not perfect so you can easily tell that the program is still running. 13. Write a program that displays all integers between low and high that are the sum of the cube of their digits. In other words, find all numbers xyz such that xyz x3 y3 z3 , for example, 153 13 53 33 . Try 100 for low and 1000 for high. 14. Write a method that returns the number of digits in an integer argument; for example, 23,498 has five digits. 15. Your freelance work with MyJava Lo-Fat Burgers was a success (see Exercise 22 of Chap. 5). The management loved your new drive-through ordering system because the customer had to order an item from each of the three menu categories. As part of a public relations campaign, however, management decided to allow a customer to skip a menu category. Modify the program to handle this option. Before you list items from each category, use a confirmation dialog to ask the customer whether he or she wants to order an item from that category. 16. Extend the program in Exercise 15 so that customers can order more than one item from each menu category. For example, the customer can buy two orders of Tofu Burgers and three orders of Buffalo Wings from the Entree menu category. 17. Complete the loan table program discussed in Section 6.8. 18. Implement the describeRules method of the Ch6HiLo class from Section 6.9. Use a confirmation dialog to ask the user whether or not to display the game rules. 19. The price table for carpet we printed out in Section 6.6 contains index values for width and length, but not labels to identify them. Write an application to generate the table shown next: 358 Chapter 6 Repetition Statements wu23399_ch06.qxd 12/14/06 17:54 Page 358
  • 386. 20. Extend the HiLo class to allow the user to designate the lower and upper bounds of the secret number. In the original HiLo class, the bounds are set to 1 and 100, respectively. 21. A formula to compute the Nth Fibonacci number was given in Exercise 19 in Chapter 3. The formula is useful in finding a number in the sequence, but a more efficient way to output a series of numbers in the sequence is to use the recurrence relation FN FN1 FN2, with the first two numbers in the sequence F1 and F2 both defined as 1. Using this recurrence relation, we can compute the first 10 Fibonacci numbers as follows: F1 = 1 F2 = 1 F3 = F2 + F1 = 1 + 1 = 2 F4 = F3 + F2 = 2 + 1 = 3 F5 = F4 + F3 = 3 + 2 = 5 F6 = F5 + F4 = 5 + 3 = 8 F7 = F6 + F5 = 8 + 5 = 13 F8 = F7 + F6 = 13 + 8 = 21 F9 = F8 + F7 = 21 + 13 = 34 F10 = F9 + F8 = 34 + 21 = 55 Write an application that accepts N, N 1, from the user and displays the first N numbers in the Fibonacci sequence. Use appropriate formatting to display the output cleanly. 22. Modify the application of Exercise 21 to generate and display all the numbers in the sequence until a number becomes larger than the value maxNumber entered by the user. 23. Improve the LoanCalculator class from Chapter 4 to accept only the valid input values for loan amount, interest rate, and loan period. The original LoanCalculator class assumed the input values were valid. For the exercise, let the loan amount between $100.00 and $1,000,000.00, the interest rate between 5 and 20 percent, and the loan period between 1 year and 30 years be valid. 24. Extend Exercise 22 on page 292 by drawing a more realistic clock. Instead of drawing a clock like this Exercises 359 wu23399_ch06.qxd 12/14/06 17:54 Page 359
  • 387. draw a circle at 5-min intervals as follows: Use a for loop to draw 12 circles. 25. In the formatting examples from the chapter, we always provided a fixed control string, such as System.out.format(%4d, 23); It is possible, however, to dynamically create the control string, as in int i = 4; System.out.format(% + i + d, 23); Using this idea of dynamically creating a control string, write a code fragment that outputs 50 X’s, using a separate line for each X. An X on a single line is preceded by two more leading spaces than the X on the previous line. The following figure shows the output for the first five lines. 26. (Optional) Write a recursive method to compute the sum of the first N positive integers. Note: This is strictly for exercise. You should not write the real method recursively. 27. (Optional) Write a recursive method to compute the sum of the first N positive odd integers. Note: This is strictly for exercise. You should not write the real method recursively. Development Exercises For the following exercises, use the incremental development methodology to implement the program. For each exercise, identify the program tasks, create a design document with class descriptions, and draw the program diagram. Map out the development steps at the start. Present any design alternatives and justify your selection. Be sure to perform adequate testing at the end of each development step. X X X X X 360 Chapter 6 Repetition Statements wu23399_ch06.qxd 12/14/06 17:54 Page 360
  • 388. 28. Write an application that draws nested N squares, where N is an input to the program. The smallest square is 10 pixels wide, and the width of each successive square increases by 10 pixels. The following pattern shows seven squares whose sides are 10, 20, 30, . . . , and 70 pixels wide. 29. The monthly payments for a given loan are divided into amounts that apply to the principal and to the interest. For example, if you make a monthly payment of $500, only a portion of the $500 goes to the principal and the remainder is the interest payment. The monthly interest is computed by multiplying the monthly interest rate by the unpaid balance. The monthly payment minus the monthly interest is the amount applied to the principal. The following table is the sample loan payment schedule for a 1-year loan of $5000 with a 12 percent annual interest rate. Payment Unpaid Total Interest No. Interest Principal Balance to Date 1 50.00 394.24 4605.76 50.00 2 46.06 398.19 4207.57 96.06 3 42.08 402.17 3805.40 138.13 4 38.05 406.19 3399.21 176.19 5 33.99 410.25 2988.96 210.18 6 29.89 414.35 2574.61 240.07 7 25.75 418.50 2156.11 265.82 8 21.56 422.68 1733.42 287.38 9 17.33 426.91 1306.51 304.71 10 13.07 431.18 875.34 317.78 11 8.75 435.49 439.85 326.53 12 4.40 439.85 0.00 330.93 Write an application that accepts a loan amount, annual interest rate, and loan period (in number of years) and displays a table with five columns: payment number, the interest and principal paid for that month, the remaining balance after the payment, and the total interest paid to date. Drawing Board not drawn to scale Exercises 361 wu23399_ch06.qxd 12/14/06 17:54 Page 361
  • 389. Note: The last payment is generally different from the monthly payment, and your application should print out the correct amount for the last payment. Use the Format class to align the output values neatly. 30. Instead of dropping a watermelon from a building, let’s shoot it from a cannon and compute its projectile. The (x, y) coordinates of a watermelon at time t are x V cos( ) . t y V sin( ) . t g 2 .t2 where g is the acceleration of gravity, V is the initial velocity, and (alpha) is the initial angle. The acceleration of gravity on earth is 9.8 m/s2 . Write an application that inputs an initial velocity V (m/s) and an initial angle alpha (degrees) and computes the projectile of a watermelon cannon ball. The program should repeat the computation until the user wants to quit. The program outputs the (x, y) oordinate value for every second, that is, t = 0, 1, 2, and so forth. The program stops the output when the y value becomes 0 or less. To use the cos and sin methods of the Math class, don’t forget that you have to convert the input angle given in degrees to radians. You can convert a degree to equivalent radians by using the following Radian degr 1 e 8 e 0 or calling the toRadians method of the Math class. Note: Air resistance is not considered in the formula. Also, we assumed the watermelon will not get smashed upon firing. 31. Write an application that simulates a slot machine. The player starts out with M coins. The value for M is an input to the program, and you charge 25 cents per coin. For each play, the player can bet 1 to 4 coins. If the player enters 0 as the number of coins to bet, then the program stops playing. At the end of the game, the program displays the number of coins left and how much the player won or lost in the dollar amount. There are three slots on the machine, and each slot will display one of the three possible pieces: BELL, GRAPE, x y (x, y) 362 Chapter 6 Repetition Statements wu23399_ch06.qxd 12/14/06 17:54 Page 362
  • 390. and CHERRY. When certain combinations appear on the slots, the machine will pay the player. The payoff combinations are as follows: Payoff (Times the Betting No. Combination Amount) 1 BELL BELL BELL 10 2 GRAPE GRAPE GRAPE 7 3 CHERRY CHERRY CHERRY 5 4 CHERRY CHERRY ----------- 3 5 CHERRY ----------- CHERRY 3 6 ----------- CHERRY CHERRY 3 7 CHERRY ----------- ----------- 1 8 ----------- CHERRY ----------- 1 9 ----------- ----------- CHERRY 1 The symbol ----------- means any piece. If the player bets 4 coins and get combination 5, for example, the machine pays the player 12 coins. Exercises 363 wu23399_ch06.qxd 12/14/06 17:54 Page 363
  • 392. Defining Your Own Classes—Part 2 O b j e c t i v e s After you have read and studied this chapter,you should be able to • Define overloaded methods and constructors. • Describe the uses of the reserved word this. • Define class methods and variables. • Describe how the arguments are passed to the parameters in method definitions with the pass-by-value scheme. • Describe how objects are returned from methods. • Document classes with javadoc comments. • Organize classes into a package. 365 7 wu23399_ch07.qxd 12/15/06 19:53 Page 365
  • 393. 366 Chapter 7 Defining Your Own Classes—Part 2 I n t r o d u c t i o n In Chapter 4, we covered the basics of programmer-defined classes with illustrative examples. There we focused our attention on straightforward cases. After seeing more sample programs in Chapters 5 and 6, we are now ready to attack advanced topics of programmer-defined classes. In addition to introducing several new topics, we will revisit some of the topics from Chapter 4 and provide a more in-depth dis- cussion. In Chapters 5 and 6, we used the Fraction class to illustrate some of the concepts. We will continue to use the Fraction class in this chapter to illustrate the key concepts introduced here. Toward the end of this chapter, we will provide a complete definition of the Fraction class. In addition to this Fraction class, we will go over other sample classes to help students master the key concepts. 7.1 Returning an Object from a Method Up until now, when we define a value-returning method, we return either primitive data types, such as int or boolean, or a String. In this section, we learn how to return objects from methods. Again, a String is an object, so in a sense, we know how to re- turn an object from a method. However, a String is treated much as a primitive datum for the most part. Here, we provide a more complete picture of what is going on when we return an object of other standard and programmer-defined classes. We use the Fraction class to illustrate the returning of an object from a method. Here’s the portion of the class definition that includes the constructor, accessors, and mutators (we will add other methods gradually as we cover more topics): class Fraction { private int numerator; private int denominator; public Fraction(int num, int denom) { setNumerator(num); setDenominator(denom); } public int getDenominator( ) { return denominator; } public int getNumerator( ) { return numerator; } return objects from methods We assume both parameters are nonnegative. We remove this assumption when listing the final version in Section 7.8. wu23399_ch07.qxd 12/15/06 19:53 Page 366
  • 394. public void setDenominator(int denom) { if (denom == 0) { //Fatal error System.err.println(Fatal Error); System.exit(1); } denominator = denom; } public void setNumerator(int num) { numerator = num; } public String toString( ) { return getNumerator() + / + getDenominator(); } //other methods come here } Notice that we do not allow a fraction to have 0 as its denominator. If there is an attempt to assign 0 as a fraction’s denominator, we will terminate the whole pro- gram. This is quite a draconian measure, but we will do it this way until we learn exception handling in Chapter 8. Now, let’s study the simplify method that reduces a fraction to its simplest form. How about the following? public void simplify( ) { int num = getNumerator(); int denom = getDenominator(); int gcd = gcd(num, denom); setNumerator(num/gcd); setDenominator(denom/gcd); } We use the gcd method that returns the greatest common divisor (int) as de- scribed in Chapter 6. We get the simplified form by dividing the numerator and the denominator by their greatest common divisor. Here’s a sample use of the method: Fraction f1 = new Fraction(24, 36); f1.simplify( ); //f1 is changed! Notice that the value of f1 is now changed because the method updates the data members of the receiving object (in this example, f1 is a receiving object because we are calling the add method of f1). Is it acceptable to change the values of the receiving object f1? In this case, is better to keep the values of f1 unchanged and to return a Fraction object that is in the simplified form. This will give flexibility to client programmers. Here’s the 7.1 Returning an Object from a Method 367 wu23399_ch07.qxd 12/15/06 19:53 Page 367
  • 395. improved version of the simplify method. (We will make an additional improvement in Section 7.8 to properly handle the case when gcd is zero.) public Fraction simplify( ) { int num = getNumerator(); int denom = getDenominator(); int gcd = gcd(num, denom); Fraction simp = new Fraction(num/gcd, denom/gcd); return simp; } The following is the sample use of the improved version: Fraction f1, f2; f1 = new Fraction(24, 36); f2 = f1.simplify( ); System.out.println(f1.toString() + can be reduced to + f2.toString()); The output will be 24/36 can be reduced to 2/3 Be aware that we can produce such output easily because we did not change the val- ues of f1. Now, if we really want to reduce f1 itself to simplest form, then we just have to assign the result back to f1 as in f1 = f1.simplify( ); Let’s study the effect of the simplify method when it returns an object. Fig- ure 7.1 shows the state of memory after the simp object is created and assigned the values. Notice that simp is a local variable, but the object itself is created in the heap memory. The return statement at the end of the simplify method returns the value of simp, which is a reference to the Fraction object. Figure 7.2 shows the state after the simplify method is complete. The value of simp is assigned to f2, and now the vari- able f2 points to this Fraction object. It is critical to realize that when we say return an object from a method, we are in fact returning a reference to the object. 368 Chapter 7 Defining Your Own Classes—Part 2 When we say“return an object from a method,”we are actually returning the address,or reference,of an object to the caller. wu23399_ch07.qxd 12/15/06 19:54 Page 368
  • 396. 7.1 Returning an Object from a Method 369 f2 = f1.simplify(); public Fraction simplify() { Fraction simp; ... simp = new Fraction(...); return simp; } At before return 1 1 f1 f2 :Fraction numerator denominator 24 36 :Fraction numerator denominator 2 3 simp Only the local variable simp is shown here. Figure 7.1 This illustration shows the state after the simp object is created and assigned with the correct values. f2 = f1.simplify(); public Fraction simplify() { Fraction simp; ... simp = new Fraction(...); return simp; } At after simplify is complete 2 2 f1 f2 :Fraction numerator denominator 24 36 :Fraction numerator denominator 2 3 simp Dotted lines indicate the memory space is deallocated. The value of simp, which is a reference to the Fraction object, is returned to the caller and assigned the variable f2. Memory space for simp is deallocated upon the completion of the method. Figure 7.2 Continuation of Figure 7.1.This illustration shows how an object (actually the reference to it) is returned to the calling program. wu23399_ch07.qxd 12/15/06 19:54 Page 369
  • 397. We will be seeing more examples of object-returning methods in this and later chapters. 370 Chapter 7 Defining Your Own Classes—Part 2 It is not necessary to create an object for every variable we use. Many novice pro- grammers often make this mistake.For example,we write Fraction f1, f2; f1 = new Fraction(24, 36); f2 = f1.simplify( ); We didn’t write Fraction f1, f2; f1 = new Fraction(24, 36); f2 = new Fraction(1, 1); //not necessary f2 = f1.simplify( ); because it is not necessary.The simplify method returns a Fraction object, and in the calling program, all we need is a name we can use to refer to this returned Fraction object.Don’t forget that the object name (variable) and the actual object instance are two separate things. 1. What’s wrong with the following declaration? class Question { Person student; public void getStudent( ) { return student; } ... } 2. Define a Vehicle class. It has a data member owner of type Person. Include an accessor to retrieve the owner person and a mutator to set the owner. 7.2 The Reserved Word this Let’s continue the implementation of the Fraction class. We now consider the four arithmetic operations for fractions; see Figure 7.3. When defining the methods for the four arithmetic operations, we introduce the use of the reserved word this. The wu23399_ch07.qxd 12/15/06 19:54 Page 370
  • 398. reserved word this is called a self-referencing pointer because it is used to refer to the receiving object of a message from within this object’s method. Let’s start with the add method that adds two fractions: public Fraction add( Fraction frac) { int a, b, c, d; Fraction sum; a = this.getNumerator(); //get the receiving b = this.getDenominator(); //object's num and denom c = frac.getNumerator(); //get frac's num d = frac.getDenominator(); //and denom sum = new Fraction(a*d + b*c, b*d); return sum; } Let’s first look at how this add method is used. The following code adds two fractions f1 and f2 and assigns the sum to f3: Fraction f1, f2, f3; f1 = new Fraction(1, 2); f2 = new Fraction(1, 4); f3 = f1.add(f2); System.out.println(Sum of + f1.toString() + and + f2.toString() + is + f3.toString(); This code, when executed, will produce the following output: Sum of 1/2 and 1/4 is 6/8 7.2 The Reserved Word this 371 self- referencing pointer Addition a b c d ad bc bd a b c d ad bc a b c d ac bd a b c d ad bc bd Subtraction Division Multiplication Figure 7.3 Rules for adding,subtracting,multiplying,and dividing fractions. Explicit use of the reserved word this Not simplified because the simplify method is not called. wu23399_ch07.qxd 12/15/06 19:54 Page 371
  • 399. In the statement f3 = f1.add(f2); we are calling the add method of f1 and passing the argument f2. So in this case, the receiving object is f1. Figure 7.4 shows the state of memory at the point where the add method of f1 is called. Notice that the self-referencing pointer this is referring to f1 because it is the receiving object. Because f2 is also a Fraction object, we can write the statement as f3 = f2.add(f1); and get the same result (since the operation is addition). In this case, the receiv- ing object is f2, and the argument is f1. Figure 7.5 shows the state of memory at the point where the add method of f2 is called. Notice how the objects referenced by frac and this are swapped. This time the self-referencing pointer this is refer- ring to f2. 372 Chapter 7 Defining Your Own Classes—Part 2 f3 = f1.add(f2); public Fraction add(Fraction frac) { Fraction sum; ... sum = new Fraction(...); return sum; } At when the method is called this points to f1 1 1 f2 f1 f3 :Fraction numerator denominator 1 4 :Fraction numerator denominator 1 2 frac this Figure 7.4 This illustration shows the state of memory for f1.add(f2). wu23399_ch07.qxd 12/15/06 19:54 Page 372
  • 400. 7.2 The Reserved Word this 373 f3 = f2.add(f1); public Fraction add(Fraction frac) { Fraction sum; ... sum = new Fraction(...); return sum; } At when the method is called this points to f2 1 1 f2 f1 f3 :Fraction numerator denominator 1 4 :Fraction numerator denominator 1 2 frac this Figure 7.5 This illustration shows the state of memory for f2.add(f1). The add method computes the sum of the receiving Fraction object and the argument Fraction object. We used the identifier frac for the parameter, so c = frac.getNumerator(); d = frac.getDenominator(); will retrieve the numerator and the denominator of the argument Fraction object. To retrieve the numerator and the denominator of the receiving Fraction object, we write a = this.getNumerator(); b = this.getDenominator(); because the reserved word this refers to the receiving object. wu23399_ch07.qxd 12/15/06 19:54 Page 373
  • 401. The use of the reserved word this is actually optional. If we do not include it explicitly, then the compiler will insert the reserved word for us. For example, if we write class Sample { public void m1( ) { ... } public void m2( ) { m1(); } } then the compiler will interpret the definition as class Sample [ public void m1( ) { ... } public void m2( ) { this.m1(); } } This is the reason why we were able to call a method from another method of the same class without the use of dot notation. In fact, it was dot notation with the reserved word this. We will discuss the use of this when referring to data members of class at the end of this section. The methods for the other three arithmetic operations are defined in a similar manner: public Fraction divide(Fraction frac) { int a, b, c, d; Fraction quotient; a = this.getNumerator(); b = this.getDenominator(); c = frac.getNumerator(); d = frac.getDenominator(); quotient = new Fraction(a*d, b*c); return quotient; } 374 Chapter 7 Defining Your Own Classes—Part 2 The reserved word this is added by the compiler. wu23399_ch07.qxd 12/15/06 19:54 Page 374
  • 402. public Fraction multiply(Fraction frac) { int a, b, c, d; Fraction product; a = this.getNumerator(); b = this.getDenominator(); c = frac.getNumerator(); d = frac.getDenominator(); product = new Fraction(a*c, b*d); return product; } public Fraction subtract(Fraction frac) { int a, b, c, d; Fraction diff; a = this.getNumerator(); b = this.getDenominator(); c = frac.getNumerator(); d = frac.getDenominator(); diff = new Fraction(a*d - b*c, b*d); return diff; } We could have defined the four arithmetic methods as void methods, instead of returning the result as a Fraction object. But doing so will severely limit the flex- ibility. Because the methods are object-returning methods, we can write a statement such as f3 = f1.add(f2); that reflects the mathematical expression f3 = f1 + f2 naturally. Moreover, because the add method returns a Fraction object as the sum, we can compose the calls to implement multiple additions. For example, the math- ematical expression f4 = f1 + f2 + f3 can be written as f4 = f1.add( f2.add(f3) ); 7.2 The Reserved Word this 375 wu23399_ch07.qxd 12/15/06 19:54 Page 375
  • 403. Bad Version where the sum of f2 and f3 is passed as an argument to the add method of f1. We can also write the expression as f4 = f1.add(f2).add(f3); because f1.add(f2) refers to a (unnamed) Fraction object, and we can call this un- named object’s add method with f3 as an argument. Another Use of this Consider the following class declaration: class MusicCD { private String artist; private String title; private String id; public MusicCD(String name1, String name2) { artist = name1; title = name2; id = artist.substring(0,2) + - + title.substring(0,9); } ... } The constructor has two String parameters, one for the artist and another for the title. An id for a MusicCD object is set to be the first two letters of the artist name followed by a hyphen and the first nine letters of the title. Now, consider what happens if we include (say, inadvertently) a local decla- ration for the identifier id like this: public MusicCD(String name1, String name2) { String id; artist = name1; title = name2; id = artist.substring(0,2) + - + title.substring(0,9); } Because there is a matching local declaration for id, the identifier refers to the local variable, not to the third data member anymore. When an identifier is encountered in a method, the following rules are applied to determine the association. 376 Chapter 7 Defining Your Own Classes—Part 2 This id is now a local variable. Local declaration for id. wu23399_ch07.qxd 12/15/06 19:54 Page 376
  • 404. Why does the Java compiler not catch such an error? When would anyone want to use the same identifier for both the data member and the local variable or parame- ter? It is true that we strongly recommend to always use an identifier different from any data member in declaring a local variable. But there is a situation in which we may want to use the same identifier for a parameter and a data member. In the MusicCD constructor, we declared the parameters name1 and name2 to avoid naming conflict. It would actually be more meaningful to use the conflicting identifiers artist and title. To do so, we rewrite the method by using the reserved word this as follows. public MusicCD(String artist, String title) { this.artist = artist; this.title = title; id = artist.substring(0,2) + - + title.substring(0,9); } Following the stated rules, the identifier artist refers to the parameter. To refer to the data member artist from within this constructor, we prefix the identifier artist with the reserved word this, using dot notation, as this.artist. In the modified con- structor, we did not use the reserved word this to refer to the data member id because it was not necessary. Its use is optional, so we could have written this.id = artist.substring(0,2) + - + title.substring(0,9); to make the code look more consistent. The reserved word this can always be used to refer to the receiving object’s data members, whether there is a naming conflict or not. 7.2 The Reserved Word this 377 Rules for associating an identifier to a local variable,a parameter,and a data member 1. If there’s a matching local variable declaration or a parameter,then the identifier refers to the local variable or the parameter. 2. Otherwise,if there’s a matching data member declaration,then the identifier refers to the data member. 3. Otherwise,it is an error because there’s no matching declaration. This refers to the data member. This refers to the parameter. Optionally,dot notation with the reserved this can be used to refer to an object’s data member from the object’s methods and constructors. wu23399_ch07.qxd 12/15/06 19:54 Page 377
  • 405. In general, following the common practice, we do not use dot notation (with the reserved word this) to refer to an object’s data members from the object’s meth- ods unless it is necessary. Note that we can also avoid the naming conflict and still use a meaningful name for a parameter by prefixing an article to the parameter. For example, we could use the identifiers anArtist and aTitle instead of name1 and name2. This nam- ing will not conflict with the data members, so the use of the reserved word this is not necessary in the constructor. As long as you use meaningful identifiers, which technique you adopt to avoid naming conflict is more of a personal preference. 378 Chapter 7 Defining Your Own Classes—Part 2 1. Write a single statement to express the following operations on fractions, using the methods from the Fraction class. f5 = (f1 + f2) / (f3 - f4) 2. If the add method is defined thus public void add(Fraction frac) { int a, b, c, d; a = this.getNumerator(); //get this fraction's b = this.getDenominator(); //num and denom c = frac.getNumerator(); //get frac's num d = frac.getDenominator(); //and denom setNumerator(a*b + c*b); //updates this setDenominator(b*d); //fraction's num and denom } why is it wrong to call the method as follows? f3 = f1.add(f2); 3. Write statements to assign the sum of fractions f1 and f2 to fraction f3, using the add method defined in question 2. 7.3 Overloaded Methods and Constructors Let’s continue to improve the Fraction class. Given the Fraction class in its cur- rent form, how can a client programmer express the following mathematical expression? f3 = 2/3 + 9 wu23399_ch07.qxd 12/15/06 19:54 Page 378
  • 406. One way is to convert the integer 9 to a fraction 9/1 and then use the add method. Fraction f1, f2, f3; f1 = new Fraction(2, 3); f2 = new Fraction(9, 1); f3 = f1.add(f2); This is not bad, but it would be nicer if we could write something like this: Fraction f1, f3; f1 = new Fraction(2, 3); f3 = f1.add(9); In other words, instead of passing a Fraction object, we want to pass a simple inte- ger value. Of course, with the current Fraction class, the statement f3 = f1.add(9); will result in an error because no matching method is defined in the class. So what we want here is two versions of addition, one that accepts a Fraction object and another that accepts an integer. Here are the two definitions: //Version 1 public Fraction add(Fraction frac) { //same as before } //Version 2 public Fraction add(int number) { Fraction sum; int a, b, c, d; a = getNumerator(); b = getDenominator(); c = number; d = 1; sum = new Fraction(a*d + c*b, b*d); return sum; } With the second add method, we now have two methods in the class that have the same name. This is not a problem as long as certain rules are met. The methods having the same name are called overloaded methods. 7.3 Overloaded Methods and Constructors 379 Including d here is redundant because its value is 1. We include it here anyway for the sake of clarity. overloaded methods wu23399_ch07.qxd 12/15/06 19:54 Page 379
  • 407. Bad Version Multiple methods can share the same name as long as one of the following rules is met: 1. They have a different number of parameters. 2. The parameters are of different data types when the number of parameters is the same. The two add methods of the Fraction class satisfy the second rule. The fol- lowing is an example in which two methods satisfy the first rule: public void myMethod(int x, int y) { ... } public void myMethod(int x) { ... } More formally, we say two methods can be overloaded if they do not have the same signature. The method signature refers to the name of the method and the number and types of its parameters. The two myMethod methods have different sig- natures because the data types of the second parameter are different. Two methods cannot be overloaded just by the different return types because two such methods would have the same signature. For example, the following two methods cannot be defined in the same class: public double getInfo(String item) { ... } public int getInfo(String item) { ... } Now, let’s look at the second add method again. Instead of defining it as we have, we can define it by calling the first add method. Here’s how: //More concise Version 2 public Fraction add(int number) { Fraction frac = new Fraction(number, 1); Fraction sum = add(frac); //calls the first add method return sum; } In defining overloaded methods, it is common for one of them to call another. Such implemenation indicates their relationship clearly—that they are different versions of the same logical operation. It also makes the modification easier because we need to change the code in only one method. Other methods calling this method require no changes. We can define the overloaded methods for the other three arithmetic operations in a similar manner. Overloading Constructors Up until now, our programmer-defined classes included exactly one constructor. But a constructor is also a method, so it, too, can be overloaded. Indeed, it is much 380 Chapter 7 Defining Your Own Classes—Part 2 method signature wu23399_ch07.qxd 12/15/06 19:54 Page 380
  • 408. more common to define multiple constructors in a programmer-defined class. The same rules for overloaded methods apply. Defining multiple constructors for a class gives the client programmer flexibility in creating instances. The client programmer can pick one of the several constructors that is suitable for her needs at hand. Let’s define multiple constructors for the Fraction class. Here are the four constructors (including the one already defined before at the bottom): public Fraction( ) { //creates 0/1 setNumerator(0); setDenominator(1); } public Fraction(int number) { //creates number/1 setNumerator(number); setDenominator(1); } public Fraction(Fraction frac) { //copy constructor setNumerator(frac.getNumerator()); setDenominator(frac.getDenominator()); } public Fraction(int num, int denom) { setNumerator(num); setDenominator(denom); } The third constructor that accepts a Fraction object and creates a copy of the passed Fraction object is called a copy constructor. A copy constructor can be quite handy when we need to create instances of a class that includes many data mem- bers. Often we want to create a copy before changing the values of or experiment- ing with the original object. As another example, here’s a Bicycle class with two constructors that initialize the two data members: class Bicycle { // Data Members private String id; private String ownerName; // Constructors public Bicycle( ) { id = XXXX-XXXX; ownerName = Unassigned; } 7.3 Overloaded Methods and Constructors 381 multiple constructors copy constructor wu23399_ch07.qxd 12/15/06 19:54 Page 381
  • 409. public Bicycle(String tagNo, String name) { id = tagNo; ownerName = name; } //the rest of the class . . . } Calling a Constructor From Another Constructor by Using this The last use of the reserved word this is to call a constructor from another con- structor of the same class. Here’s how we can rewrite the four constructors of the Fraction class by using the reserved word this: public Fraction( ) { //creates 0/1 this(0, 1); } public Fraction(int number) { //creates number/1 this(number, 1); } public Fraction(Fraction frac) { //copy constructor this( frac.getNumerator(), frac.getDenominator() ); } public Fraction(int num, int denom) { setNumerator(num); setDenominator(denom); } The syntax for calling a constructor from another constructor of the same class is this( parameter-list ); The constructor that matches the parameter list will be called. We can add more statements after the this statement in a constructor, but not before it. In other words, the call to this in a constructor must be the first statement of the constructor. 382 Chapter 7 Defining Your Own Classes—Part 2 This constructor is called by the other three constructors. When you use this to call a constructor from another constructor of the same class, the this statement must be the first statement in the constructor. wu23399_ch07.qxd 12/15/06 19:54 Page 382
  • 410. 7.4 Class Variables and Methods 383 1. Are there any conflicts in the following three constructors for ClassX to be valid? public ClassX( int X ) { ... } public ClassX( float X ) { ... } public ClassX( int Y ) { ... } 2. Define a Student class. A Student has a name. Define two constructors, one with no argument and another with the name as its argument. Initialize the name to a default value Unknown for the zero-argument constructor. 3. Rewrite the following constructors, so the first one calls the second one. public ClassOne(int alpha) { this.alpha = alpha; this.beat = 0; } public ClassOne(int alpha, int beta) { this.alpha = alpha; this.beta = beta; } 7.4 Class Variables and Methods We introduced the concepts of class methods, class variables, and class constants in Chapter 1. We saw how class constants are declared in the actual Java statements in Chapter 4. We complete our study of class components in this section by describing how class methods and class variables are used in Java programs. Let’s begin with the class methods. The Math class includes a class method called min to compare two numbers. We use this class method as follows: int i, j, smaller; i = ...; j = ...; smaller = Math.min(i, j); wu23399_ch07.qxd 12/15/06 19:54 Page 383
  • 411. Now suppose we want to have a method to find the smaller of two Fraction objects. Where do we define such a method? The logical place is, of course, the Fraction class. But will this method be an instance method? No, a class method, which fol- lows the pattern of the min method of the Math class, is most appropriate. We can define a class method called min that accepts two Fraction objects as arguments and returns the smaller fraction. Here’s how we define the min method: class Fraction { ... public static Fraction min(Fraction f1, Fraction f2) { //convert to decimals and then compare double f1_dec = f1.decimal(); double f2_dec = f2.decimal(); if ( f1_dec = f2_dec) { return f1; } else { return f2; } } private double decimal( ) { //returns the decimal equivalent return (double) getNumerator() / getDenominator(); } ... } The reserved word static indicates that the min method is a class method. A class method is called by using dot notation with the class name. Here’s a sample use: Fraction f1, f2, smaller; f1 = new Fraction(1, 6); f2 = new Fraction(4, 5); smaller = Fraction.min(f1, f2); Remember, in Chapter 6 we discussed the need for finding the greatest com- mon divisor of two integers to simplify a given fraction. Following the logic of the min method, we can define the gcd method as a class method. Here’s how: public static int gcd(int m, int n) { //the code implementing the Euclidean algorithm } 384 Chapter 7 Defining Your Own Classes—Part 2 wu23399_ch07.qxd 12/15/06 19:54 Page 384
  • 412. Notice that the arguments to this method are two integers. When this method is called from another method of the Fraction class, the numerator and the denomina- tor are passed as the arguments. We declare this method public so the client pro- grammers of the Fraction class can use it also. If this is not necessary, then we can declare it private. ( Note: Logically, the gcd method should be a class method, but there will be no serious consequences if we define it as an instance method.) In a manner similar to the min and gcd methods, we can define the methods for arithmetic operations as class methods. For example, here’s how: public static Fraction add(Fraction f1, Fraction f2) { int a, b, c, d; Fraction sum; a = f1.getNumerator(); b = f1.getDenominator(); c = f2.getNumerator(); d = f2.getDenominator(); sum = new Fraction(a*d + b*c, b*d); return sum; } To use this class method, we write something like this: Fraction x = new Fraction(1, 8); Fraction y = new Fraction(4, 9); Fraction sum = Fraction.add(x, y); The class method add, however, becomes awkward when we try to compose additions. To add three fractions x, y, and z, for example, we have to write Fraction sum = Fraction.add(Fraction.add(x,y), z); The instance method add, as we defined at the beginning of the chapter, allows a lot more natural and flexible use. Now let’s look at an example of class variables (we have been using class con- stants since Chap. 4). Suppose we want to assign a tag number automatically when a new instance of the Bicycle class is created. We want the tag numbers to be ABC-101, ABC-102, ABC-103, and so forth. What we need to define in the Bicycle class is a counter that counts up from 101. Only one counter is necessary for the whole class, so it is logical to define this counter as a class variable. First, we declare and initialize the class variable counter: class Bicycle { private static int counter = 101; ... } 7.4 Class Variables and Methods 385 wu23399_ch07.qxd 12/15/06 19:54 Page 385
  • 413. Then we adjust the constructor, so the id of a bicycle is assigned correctly. Here’s how: public Bicycle( ) { id = ABC- + counter; ... counter++; } Static Initializer There are cases in which we may need to do more than a simple assignment to ini- tialize a class variable. For example, we may be required to read the starting value for the class variable counter of the Bicycle class from a file. If we need to perform more than a simple assignment to initialize a class variable, then we define a static initializer. A static initializer is a code that gets executed when a class is loaded into the Java system. It is defined in the following manner: class XYZ { ... static { //code to initialize //class variables and perform //other tasks } ... } As an illustration, here’s how we define the static initializer for the Bicycle class to set the starting value of counter to 101: class Bicycle { private static int counter; ... static { counter = 101; } ... } We conclude this section with important reminders. 386 Chapter 7 Defining Your Own Classes—Part 2 static initializer wu23399_ch07.qxd 12/15/06 19:54 Page 386
  • 414. 7.5 Call-by-Value Parameter Passing We will provide more detailed coverage on how arguments are passed to a method. Let’s first review some key facts. Local variables are used for temporary purposes, such as storing intermediate results of a computation. While the data members of a class are accessible from all instance methods of the class, local variables and para- meters are accessible only from the method in which they are declared, and they are available only while the method is being executed. Memory space for local vari- ables and parameters is allocated upon declaration and at the beginning of the method, respectively, and erased upon exiting from the method. When a method is called, the value of the argument is passed to the matching parameter, and separate memory space is allocated to store this value. This way of passing the value of arguments is called a pass-by-value or call-by-value scheme. Since separate memory space is allocated for each parameter during the execution of the method, the parameter is local to the method, and therefore changes made to the parameter will not affect the value of the corresponding argument. Consider the following myMethod method of the Tester class. The method does not do anything meaningful. We use it here to illustrate how the call-by-value scheme works. class Tester { public void myMethod(int one, double two ) { one = 25; two = 35.4; } } What will be the output from the following code? Tester tester; int x, y; tester = new Tester(); x = 10; y = 20; tester.myMethod( x, y ); System.out.println( x + + y ); 7.5 Call-by-Value Parameter Passing 387 1. Class methods can access only the class variables and the class constants of the class. 2. Instance methods,including constructors,can access all types of data members. 3. Class methods cannot call instance methods of the same class. 4. Instance methods can call all other methods of the same class. call-by-value scheme wu23399_ch07.qxd 12/15/06 19:54 Page 387
  • 415. The output will be 10 20 because with the pass-by-value scheme, the values of arguments are passed to the parameters, but changes made to the parameters are not passed back to the argu- ments. Figure 7.6 shows how the pass-by-value scheme works. Notice that the arguments are matched against the parameters in the left-to- right order; that is, the value of the leftmost argument is passed to the leftmost pa- rameter, the value of the second-leftmost argument is passed to the second-leftmost parameter, and so forth. The number of arguments in the method call must match the number of parameters in the method definition. For example, the following calls to myMethod of the Tester class are all invalid because the number of arguments and number of parameters do not match. tester.myMethod( 12 ); tester.myMethod( x, y, 24.5); Since we are assigning the value of an argument to the matching parameter, the data type of an argument must be assignment-compatible with the data type of the matching parameter. For example, we can pass an integer argument to a float pa- rameter, but not vice versa. In the following, the first call is valid, but the second one is invalid: tester.myMethod( 12, 25 ); tester.myMethod( 23.0, 34.5 ); The name of the parameter and the name of the argument can be the same. Keep in mind, however, that the values of arguments are still passed to a method by the pass-by-value scheme; that is, local copies are made whether the argument and the parameter share the same name or not. 388 Chapter 7 Defining Your Own Classes—Part 2 Remember these key points about arguments and parameters: 1. Arguments are passed to a method by using the pass-by- value scheme. 2. Arguments are matched to the parameters from left to right.The data type of an argument must be assignment-compatible with the data type of the matching parameter. 3. The number of arguments in the method call must match the number of parameters in the method definition. 4. Parameters and arguments do not have to have the same name. 5. Local copies,which are distinct from arguments,are created even if the parameters and arguments share the same name. 6. Parameters are input to a method,and they are local to the method.Changes made to the parameters will not affect the value of corresponding arguments. wu23399_ch07.qxd 12/15/06 19:54 Page 388
  • 416. 7.5 Call-by-Value Parameter Passing 389 Figure 7.6 How memory space for the parameters is allocated and deallocated. 1 execution flow Local variables do not exist before the method execution. x = 10; y = 20; tester.myMethod( x, y ); public void myMethod( int one, double two ) { one = 25; two = 35.4; } at before calling myMethod 1 1 10 x 20 y state of memory 2 Memory space for myMethod is allocated, and the values of arguments are copied to the parameters. x = 10; y = 20; tester.myMethod( x, y ); public void myMethod( int one, double two ) { one = 25; two = 35.4; } values are copied at 2 2 10 x 20 y 10 one 20.0 two 3 The values of parameters are changed. x = 10; y = 20; tester.myMethod( x, y ); public void myMethod( int one, double two ) { one = 25; two = 35.4; } at before return 3 3 10 x 20 y 25 one 35.4 two 4 Memory space for myMethod is deallocated, and parameters are erased. Arguments are unchanged. x = 10; y = 20; tester.myMethod( x, y ); public void myMethod( int one, double two ) { one = 25; two = 35.4; } at after myMethod 4 4 10 x 20 y wu23399_ch07.qxd 12/15/06 19:54 Page 389
  • 417. Now let’s look at a similar example again, but this time with objects. Consider the following class: class ObjectTester { public void swap(Fraction f1, Fraction f2) { Fraction temp; temp = f1; //swap the two fractions f1 = f2; f2 = temp; } } What will be the output from the following code? ObjectTester tester; Fraction x, y; tester = new ObjectTester(); x = new Fraction(1, 2); y = new Fraction(3, 4); tester.swap(x, y); System.out.println(x = + x.toString()); System.out.println(y = + y.toString()); The output will be x = 1/2 y = 3/4 because the changes made to the parameters are not passed back to the arguments. It does not matter whether we are passing primitive data values or objects (actually, references to the objects). Figure 7.7 shows the effect of calling the swap method. Changes made to the parameters are not passed back to the arguments, but when we are passing objects to a method, then the changes made to the object itself are reflected back to the caller because the calling side still has the same reference to the object. Let’s look at an example. Consider the following class: class ObjectTester2 { public void change(Fraction f1) { f1.setNumerator(10); } } 390 Chapter 7 Defining Your Own Classes—Part 2 wu23399_ch07.qxd 12/15/06 19:54 Page 390
  • 418. 7.5 Call-by-Value Parameter Passing 391 Figure 7.7 This illustration shows the effect of calling the swap method. :Fraction numerator denominator 3 4 :Fraction numerator denominator 1 2 f1 f2 x y :Fraction numerator denominator 3 4 :Fraction numerator denominator 1 2 f1 f2 x y :Fraction numerator denominator 3 4 :Fraction numerator denominator 1 2 f1 f2 x y At the beginning of the swap method. At the end of the swap method when f1 and f2 are swapped. After the swap method terminates. No changes made to parameters f1 and f2 are reflected back to the arguments. Both x and y still point to the same objects as before the call. wu23399_ch07.qxd 12/15/06 19:54 Page 391
  • 419. What will be the output from the following code? ObjectTester2 tester; Fraction x; tester = new ObjectTester(); x = new Fraction(1, 2); tester.change(x); System.out.println(x = + x.toString()); The output will be x = 10/2 Figure 7.8 shows the effect of calling the change method. Notice that the variable x continues to point to the same object, but the object itself has been modified. 392 Chapter 7 Defining Your Own Classes—Part 2 Pass-by-value (also known as call-by-value) is the only parameter passing mech- anism Java supports. Because we are passing references when objects are passed to methods, many people with background in other programming languages use the term pass by reference (or call by reference) when referring to the passing of objects to methods.This is wrong. Pass by reference means an address (or ref- erence) of a variable is passed, whereas pass by value means the content of a variable is passed (and copied into a parameter). In Java, the content of a variable is either a value of primitive data type or a reference to an object (this is the source of confusion). But it doesn’t matter what the content of a variable is; as long as the content of a variable is passed and copied into a parameter, it is a call by value. If a programming language supports the pass-by-reference mechanism,then it is possible,for example,to swap the values of two arguments in a single method call.No such thing is possible in Java. 1. What is the name of the scheme used in Java to pass arguments to a method? 2. What is the output from the following code? class Question { private int one; public void myMethod( int one ) { this.one = one; one = 12; } } class Test { public static void main(String[] arg) { int one = 30; wu23399_ch07.qxd 12/15/06 19:54 Page 392
  • 420. 7.5 Call-by-Value Parameter Passing 393 Figure 7.8 This illustration shows the effect of calling the change method. :Fraction numerator denominator 1 2 Numerator is changed to 10. At the beginning of the change method. At the end of the change method when the numerator of the Fraction object is changed. After the change method terminates. The variable x still points to the same object, but the object itself has been modified. f1 x :Fraction numerator denominator 10 2 f1 x :Fraction numerator denominator 10 2 f1 x Question q = new Question(); q.myMethod(one); System.out.println(one); } } wu23399_ch07.qxd 12/15/06 19:54 Page 393
  • 421. 7.6 Organizing Classes into a Package For simplicity, we have placed all programmer-defined classes of a program in the same folder since Chapter 4. This approach works fine while we are learning pro- gramming and do not deal with many classes. But in a more real-life context, we need to manage classes more effectively. For example, following the approach, we have to copy the Fraction class to multiple folders if we want to use this class in dif- ferent programs. The correct approach to reusing programmer-defined classes is to organize them into packages, just as the standard classes are organized into packages. We illustrate the process by using the Fraction class. Let’s name the package to place the Fraction class myutil. It is a Java convention to name the package with all low- ercase letters. Once this package is set up correctly, we can use the classes in the package by importing it, just as we have been doing with the standard packages. import myutil.*; class MyClient { Fraction f1; ... } To set up the programmer-defined packages for general reuse, not just use by the programs in the same folder, we have to perform the following tasks: 1. Include the statement package myutil; as the first statement of the source file for the Fraction class. 2. The class declaration must include the visibility modifier public as public class Fraction { ... } 3. Create a folder named myutil, the same name as the package name. In Java, the package must have a one-to-one correspondence with the folder. 4. Place the modified Fraction class into the myutil folder and compile it. 5. Modify the CLASSPATH environment variable to include the folder that contains the myutil folder. See below. Step 5 is the most troublesome step for those new to Java. Since the exact steps to change the CLASSPATH environment variable are different from each platform (Windows, Unix, Mac) and Java IDE (Eclipse, NetBeans, jGRASP, BlueJ, etc.), we will describe only the general idea for the Windows platform here. Suppose we have a folder named JavaPrograms under the C: drive, and the myutil package (folder) is placed inside this JavaPrograms folder. Then to use the classes in 394 Chapter 7 Defining Your Own Classes—Part 2 programmer- defined packages wu23399_ch07.qxd 12/15/06 19:54 Page 394
  • 422. the myutil package, the classpath environment should make a reference to the JavaPrograms folder (not to the package myutil itself): set classpath=.;c:JavaPrograms The period after the equals symbol refers to the current folder (the folder where the client program we are trying to execute is located). Without this reference to the cur- rent folder, the client program will not recognize other classes in the same folder. 7.7 Using Javadoc Comments for Class Documentation 395 To make the programmer-defined packages accessible to all client programs,the CLASSPATH environment variable must be set up correctly. 7.7 Using Javadoc Comments for Class Documentation We mentioned in Chapter 2 that there are three styles of comments in Java. We have been using the two of them. We introduce the third style called javadoc comments in this section. Many of the programmer-defined classes we design are intended to be used by other programmers. It is, therefore, very important to provide meaningful documentation to the client programmers so they can understand how to use our classes correctly. By adding javadoc comments to the classes we design, we can provide a consistent style of documenting the classes. Once the javadoc comments are added to a class, we can use a special program (comes as a part of Java 2 SDK) to generate HTML files for documentation. (Note: An HTML file is a specially marked file intended for a Web browser.) We mentioned in Chapter 2 that the docu- mentation for the standard classes can be found at http://java.sun.com/j2se/1.5/ docs/api/index.html. This documentation is derived from the javadoc comments embedded in the standard classes. We will describe how to use javadoc comments and generate the corresponding- HTMLdocumentation files. Before we get into the details, we first show the end result so you can visualize where the process is leading. Figure 7.9 shows a portion of the HTML documentation for the Fraction class displayed in a browser. A javadoc comment is used as header comment for a class, a data member, or a method. Let’s begin with the class header comment for the Fraction class in the javadoc format: /** * An instance of this class represents a fraction. * * @author Dr. Caffeine * */ class Fraction { ... } javadoc comments wu23399_ch07.qxd 12/15/06 19:54 Page 395
  • 423. The javadoc comments begin with the marker /** and end with the marker */. The asterisks on the lines between the first and the last markers have no significance; they are there to provide a visual aid to highlight the comments in the program. It is an accepted standard to use the asterisks in this manner for the javadoc comments. Inside the javadoc comments, we can use a number of javadoc tags, special markers that begin with the @ mark. In this example, we see one javadoc tag @author, which we use to list the authors of the class. 396 Chapter 7 Defining Your Own Classes—Part 2 Figure 7.9 A browser showing the HTML documentation file derived from the javadoc comments for the Fraction class. javadoc tags @author tag wu23399_ch07.qxd 12/15/06 19:54 Page 396
  • 424. Here’s how we comment a data member in javadoc: /** * The numerator portion of this fraction */ private int numerator; When the length of a comment is short and fits in a single line, then we can write the javadoc comment as /** The numerator portion of this fraction */ private int numerator; The javadoc comment for a method is similar to the one for the class header comment. It will include a number of javadoc tags in addition to a general descrip- tion. Here’s how a method is commented by using javadoc: /** * Returns the sum of this Fraction * and the parameter frac. The sum * returned is NOT simplified. * * @param frac the Fraction to add to this * Fraction * * @return the sum of this and frac */ public Fraction add(Fraction frac) { ... } The purpose of the method header comment is to record the method’s pur- pose, list of parameters passed to the method, and value returned from the method. This method receives one parameter, so there is one @param tag. We attach a short description of the parameter in addition to the parameter’s name. The syntax for the @param javadoc tag is @param parameter name description The description portion can go beyond one line. As this method returns a value, we add the @return tag. Its syntax is @return description A javadoc comment for a constructor is defined in a manner similar to the one for a method, except there will never be an @return tag for a constructor. Figure 7.10 shows the HTML document that is generated from this javadoc com- ment. Notice the effect of @param and @return tags. 7.7 Using Javadoc Comments for Class Documentation 397 @param tag @return tag wu23399_ch07.qxd 12/15/06 19:54 Page 397
  • 425. The use of the javadoc comments does not preclude the use of other types of comments. We still need to use regular comments to describe the code as necessary. For example, we will continue to include the group comment for methods, as in so that programmers reading the class will have a handy reference to the list of methods without referring to any online documentation. This is especially useful when the programmers are reading a hard copy of the class source file. Notice that we don’t use the javadoc style for a quick reference list because javadoc comments are used only for describing the class and its data members and methods. Once all the javadoc comments are added to a class, we are ready to generate the corresponding HTML documentation file. For easy reference, we call it the //–––––––––––––––––––––––––––––––––––––––––– // Public Methods: // // Fraction add ( Fraction ) // Fraction add ( int ) // // ... //–––––––––––––––––––––––––––––––––––––––––– 398 Chapter 7 Defining Your Own Classes—Part 2 Figure 7.10 The portion of the HTML documentation file that is derived from the javadoc header comment for the add method. wu23399_ch07.qxd 12/15/06 19:54 Page 398
  • 426. javadoc file. Many Java editors and IDEs include a menu option that you can use to generate javadoc files easily and quickly. Here we describe the steps you can take to generate javadoc files using the minimalist approach (see App. A). In the command prompt window, we used the commands javac and java to compile and run Java programs, respectively. Similarly, to generate javadoc files, we use the javadoc command. For example, to generate a javadoc file for the Fraction class, we enter javadoc -private Fraction.java We specify the -private option because we want to generate the documentation for all types of methods (so far, we have covered two of these—private and public). The -private option generates the most complete documentation. When the command is executed, status messages such as these are displayed. After the command is executed successfully, there will actually be a collection of HTML files, not just the expected Fraction.html. You can view the content shown in Figure 7.9 by opening the file index.html and clicking the Fraction link. Open the Fraction.html directly from your browser and see the difference. We encourage you to open other HTML files to see how these files are related. The generated HTML files are located in the same directory where the source file Fraction.java is located. You can change the directory by setting the -d option and specifying the directory to store the generated HTML files (alternatively, you can move the files using an op- erating system’s file manager). We ordinarily do not generate javadoc files one class at a time. Rather, it is more common to generate a complete set of javadoc files for all classes in a single package at once, as in javadoc -private *.java We will refer you to websites for a more complete discussion of javadoc. 7.7 Using Javadoc Comments for Class Documentation 399 wu23399_ch07.qxd 12/15/06 19:54 Page 399
  • 427. 7.8 The Complete Fraction Class In this section, we will list a complete definition for the myutil.Fraction class. In the final version of the class, we will include improvements to one of the constructors and the simplify method. Earlier in the chapter, we presented the fourth constructor as follows: public Fraction(int num, int denom) { setNumerator(num); setDenominator(denom); } 400 Chapter 7 Defining Your Own Classes—Part 2 General information on javadoc is located at http://java.sun.com/j2se/javadoc Detailed reference on how to use javadoc on Windows is located at http://java.sun.com/j2se/1.5/docs/tooldocs/windows/javadoc.html Is it really important to use javadoc comments? It’s true that we have to learn a few extra items to use javadoc comments,but the benefits warrant a little extra effort. First, by using javadoc comments, we can easily produce the standard online documentation. Even if we don’t have an immediate need to produce an online documentation, we can use javadoc comments because they are really not that different from other styles of commenting, and their use gives us an option to produce an online documentation later.Second,since javadoc is a standard,other programmers will have an easier time reading your code with javadoc comments than reading code with a nonstandard style of comments. 1. Add javadoc comments to the following class. class Instructor { private String name; public void setName(String name) { this.name = name; } public String getName( ) { return name; } } 2. What is the purpose of @author tag? wu23399_ch07.qxd 12/15/06 19:54 Page 400
  • 428. For this constructor to function properly, we made an assumption that the values for both parameters are nonnegative. Let’s remove this assumption and make the necessary modifications to the constructor. Consider the following two statements: Fraction f1 = new Fraction(-2, 9); Fraction f2 = new Fraction(2, -9); Both represent the same value, namely, 2 9 . With the given constructor, f1 will have the values 2 and 9 for its data members numerator and denominator, respectively. And f2 will have the values 2 and 9 for its data members numerator and denom- inator, respectively. This means that we have two distinct ways to represent the same value. It is always preferable to maintain a consistent representation because multiple representations for the same value would lead to a more complex code for handling different representations correctly. We will improve this constructor so that a negative fraction is always represented by a negative value for numerator and a positive value for denominator. Now, consider the following two statements: Fraction f3 = new Fraction(2, 9); Fraction f4 = new Fraction(-2, -9); Both objects represent the same positive fraction 2 9 . Again, to maintain consistent representation, a positive fraction is always represented by positive values for both numerator and denominator. Finally, consider the following two statements: Fraction f3 = new Fraction(0, 9); Fraction f4 = new Fraction(0, -5); Both objects represent the numerical value of 0. We will always represent the numer- ical value of 0 by storing 0 in the data member numerator and 1 in denominator. Here’s the modified constructor: public Fraction(int num, int denom) { if (denom 0) { num = -num; denom = -denom; } if (num == 0) { denom = 1; } setNumerator(num); setDenominator(denom); } 7.8 The Complete Fraction Class 401 wu23399_ch07.qxd 12/15/06 19:54 Page 401
  • 429. We will also make a modification to the simplify method. The original simplify method fails when someone tries to simplify a zero fraction (i.e., a fraction with numerical value of 0). To reduce a fraction to its simplified form, we find the greatest common divisor of its numerator and denominator and divide them by the greatest common divisor. What happens when the numerator is 0? The greatest common divisor of 0 and any other value is 0. So we would end up dividing the numerator and denominator by 0! Here’s the new simplify method that avoids this problem: public Fraction simplify( ) { int num = getNumerator(); int denom = getDenominator(); int divisor = 1; if (num != 0) { divisor = gcd(Math.abs(num), denom); } return new Fraction(num/divisor, denom/divisor); } 402 Chapter 7 Defining Your Own Classes—Part 2 package myutil; /** * An instance of this class represents a fraction. * * */ public class Fraction { /** the numerator of this fraction */ private int numerator; /** the denominator of this fraction */ private int denominator; //----------------------------------------- // Constructors //----------------------------------------- /** * Creates a fraction 0/1 */ public Fraction( ) { this(0, 1); } Data Members Constructors wu23399_ch07.qxd 12/15/06 19:54 Page 402
  • 430. 7.8 The Complete Fraction Class 403 /** * Creates a fraction number/1 * * @param number the numerator */ public Fraction(int number) { this(number, 1); } /** * Creates a copy of frac * * @param frac a copy of this parameter is created */ public Fraction(Fraction frac) { this(frac.getNumerator(), frac.getDenominator()); } /** * Creates a fraction num/denom. Create a negative * fraction as -num and denom. If negative values * are specified for both num and denom, the fraction * is converted to a positive. If num is positive and * denom is negative, the fraction will be converted to * have negative num and positive denom. * When the num is zero, denom is set to 1. Zero is * always represented as 0/1 * * @param num the numerator * @param denom the denominator */ public Fraction(int num, int denom) { if (denom 0) { num = -num; denom = -denom; } if (num == 0) { denom = 1; } setNumerator(num); setDenominator(denom); } //----------------------------------------- // Class Methods // //----------------------------------------- wu23399_ch07.qxd 12/15/06 19:54 Page 403
  • 431. /** * Returns the greatest common divisor of * the parameters m and n * * @param m the first number * @param n the second number * * @return the greatest common divisor of m and n */ public static int gcd(int m, int n) { int r = n % m; while (r !=0) { n = m; m = r; r = n % m; } return m; } /** * Returns the smaller of the two parameters f1 and f2 * * @param f1 the first fraction to compare * @param f2 the second fraction to compare * * @return the smaller of the two parameters */ public static Fraction min(Fraction f1, Fraction f2) { //convert to decimals and then compare double f1_dec = f1.decimal(); double f2_dec = f2.decimal(); if (f1_dec = f2_dec) { return f1; } else { return f2; } } 404 Chapter 7 Defining Your Own Classes—Part 2 gcd min wu23399_ch07.qxd 1/12/07 10:48 Page 404
  • 432. //----------------------------------------- // Public Instance Methods // //----------------------------------------- /** * Returns the sum of this Fraction * and the parameter frac. The sum * returned is NOT simplified. * * @param frac the Fraction to add to this * Fraction * * @return the sum of this and frac */ public Fraction add(Fraction frac) { int a, b, c, d; Fraction sum; a = this.getNumerator(); b = this.getDenominator(); c = frac.getNumerator(); d = frac.getDenominator(); sum = new Fraction(a*d + b*c, b*d); return sum; } /** * Returns the sum of this Fraction * and the int parameter number. The sum * returned is NOT simplified. * * @param number the integer to add to this * Fraction * @return the sum of this Fraction and number */ public Fraction add(int number) { Fraction frac = new Fraction(number, 1); Fraction sum = add(frac); return sum; } 7.8 The Complete Fraction Class 405 add add wu23399_ch07.qxd 12/15/06 19:54 Page 405
  • 433. /** * Returns the quotient of this Fraction * divided by the parameter frac. The quotient * returned is NOT simplified. * * @param frac the divisor of the division * * @return the quotient of this fraction * divided by frac */ public Fraction divide(Fraction frac) { int a, b, c, d; Fraction quotient; a = this.getNumerator(); b = this.getDenominator(); c = frac.getNumerator(); d = frac.getDenominator(); quotient = new Fraction(a*d, b*c); return quotient; } /** * Returns the quotient of this Fraction * divided by the int parameter number. The quotient * returned is NOT simplified. * * @param number the divisor * * @return the quotient of this Fraction divided by number */ public Fraction divide(int number) { Fraction frac = new Fraction(number, 1); Fraction quotient = divide(frac); return quotient; } /** * Compares this fraction and the parameter frac for * equality. This method compares the two by first * reducing them to the simplest form. * * @param frac the fraction object to compare * * @return true if this Fraction object and frac are equal */ 406 Chapter 7 Defining Your Own Classes—Part 2 divide divide wu23399_ch07.qxd 12/15/06 19:54 Page 406
  • 434. public boolean equals(Fraction frac) { Fraction f1 = simplify(); //simplify itself Fraction f2 = frac.simplify(); //simplify frac return (f1.getNumerator() == f2.getNumerator() f1.getDenominator() == f2.getDenominator()); } /** * Returns the denominator of this fraction * * @return the denominator of this fraction */ public int getDenominator( ) { return denominator; } /** * Returns the numerator of this fraction * * @return the numerator of this fraction */ public int getNumerator( ) { return numerator; } /** * Returns the product of this Fraction * and the parameter frac. The product * returned is NOT simplified. * * @param frac the multiplier of the multiplication * * @return the product of this fraction * and the parameter frac */ public Fraction multiply(Fraction frac) { int a, b, c, d; Fraction product; a = this.getNumerator(); b = this.getDenominator(); c = frac.getNumerator(); d = frac.getDenominator(); product = new Fraction(a*c, b*d); return product; } 7.8 The Complete Fraction Class 407 equals getDenominator getNumerator multiply wu23399_ch07.qxd 12/15/06 19:54 Page 407
  • 435. /** * Returns the product of this Fraction * and the int parameter number. The product * returned is NOT simplified. * * @param number the multiplier * * @return the product of this Fraction and number */ public Fraction multiply(int number) { Fraction frac = new Fraction(number, 1); Fraction product = multiply(frac); return product; } /** * Sets the denominator of this fraction * * @param denom the denominator of this fraction */ public void setDenominator(int denom) { if (denom == 0) { //Fatal error System.out.println(Fatal Error); System.exit(1); } denominator = denom; } /** * Sets the numerator of this fraction * * @param num the numerator of this fraction */ public void setNumerator(int num) { numerator = num; } /** * Returns a new Fraction object that is in * the simplest form of this Fraction object. If * this Fraction is zero, then a simple copy of * it is returned. * * @return a Fraction object in the simplest form * of this Fraction */ 408 Chapter 7 Defining Your Own Classes—Part 2 multiply setDenominator setNumerator wu23399_ch07.qxd 12/15/06 19:54 Page 408
  • 436. public Fraction simplify( ) { int num = getNumerator(); int denom = getDenominator(); int divisor = 1; if (num != 0) { divisor = gcd(Math.abs(num), denom); } return new Fraction(num/divisor, denom/divisor); } /** * Returns the difference of this Fraction * and the parameter frac. The difference * returned is NOT simplified. * * @param frac the Fraction to subtract from * this Fraction * * @return the difference of this and frac */ public Fraction subtract(Fraction frac) { int a, b, c, d; Fraction diff; a = this.getNumerator(); b = this.getDenominator(); c = frac.getNumerator(); d = frac.getDenominator(); diff = new Fraction(a*d - b*c, b*d); return diff; } /** * Returns the difference of this Fraction * and the int parameter number. The difference * returned is NOT simplified. * * @param number the int value to subtract * * @return the difference of this and number */ public Fraction subtract(int number) { Fraction frac = new Fraction(number, 1); Fraction difference = subtract(frac); return difference; } 7.8 The Complete Fraction Class 409 simplify subtract subtract wu23399_ch07.qxd 12/15/06 19:54 Page 409
  • 437. /** * Returns the String representation of this Fraction * * @return the String representation of this Fraction */ public String toString( ) { return getNumerator() + / + getDenominator(); } //----------------------------------------- // Private Methods // //----------------------------------------- //** * Returns the decimal equivalent of this fraction * * @return the decimal equivalent of this fraction */ private double decimal( ) { //returns the decimal equivalent return (double) getNumerator() / getDenominator(); } } 410 Chapter 7 Defining Your Own Classes—Part 2 toString decimal Library Overdue Checker How many library books are lying around in your room waiting to be returned? How much have you accrued in late charges on those overdue books? Let’s write a program that computes the total charges for overdue library books. The program allows you to input the title,overdue charge per day,maximum possible charge,and due date for each book you enter.The due date is the only required input.The other three input values are optional,and when they are not provided,preset default values are used by the program. We assume that an upper limit is set for overdue charges,so your charge will not increase beyond this limit. This limit is entered as the maximum possible charge. For example, a library may set $1.50 as the overdue charge per day and $30 as the maximum overdue charge for a single overdue book.We enter the overdue charge per day and the maximum overdue charge for every book,because depending on the types of books,they could be different. For example, a charge for books with a 3-day loan period may be much higher than for books with a regular 2-week loan period. After you enter information for all books, the program displays the entered book data.Then the program will allow you to enter different return dates.For each return date you enter,the program will display the total overdue charges.Being able to enter different Sample Development 7.9 Sample Development wu23399_ch07.qxd 12/15/06 19:54 Page 410
  • 438. return dates will let you make an informed decision,such as“I’ll wait till tomorrow since it’s raining heavily today and it costs only $2 more if I return them tomorrow.” (We always encourage you to return library books promptly for the sake of fellow library users.) A better program will warn you when there’s a looming due date so you won’t end up paying the overdue charges.We will discuss this and other possible extensions at the end of this section.All the possible extensions we will discuss require techniques yet to be studied. The program we develop here is implemented by the techniques we have already mastered and by using one simple helper class. Problem Statement Write an application that computes the total charges for the overdue library books. For each library book, the user enters the due date and (optionally) the overdue charge per day,the maximum charge,and the title.If the optional values are not entered, then the preset default values are used. A complete list of book information is displayed when the user finishes entering the input data.The user can enter different return dates to compare the overdue charges. Overall Plan As always, we begin our overall plan for the development with the outline of program logic.We first let the user enter the information on all books.After finishing the book data entry, we display them as a list.Then we ask repeatedly for return dates. For each return date entered, we provide the total overdue charge.We express the program flow as hav- ing three tasks: 1. Get the information for all books. 2. Display the entered book data. 3. Ask for the return date and display the total charge.Repeat this step until the user quits. Let’s look at each task and determine objects required for handling the task. The first step sounds simple enough, but it hides the complexity of the whole program. It indicates the need for at least three types of objects. One is to carry out the actual input routines, another is to retain four pieces of information for each book, and yet another is to keep track of multiple books entered by the user. Notice that there’s no limit on the number of books the user can enter,because putting such a limit will reduce the usability of the program.This means we need a class to manage a collection of book information. We have not yet learned how to manage a collection of objects (Chap. 10 covers the topic), so we will use the helper class named BookTracker. This class is actually very straightforward,once we learn the relevant topic.The class is written generically and does not contain much application-specific logic. We will define a class named LibraryBook that keeps track of book information.An instance of this class represents a single library book. The LibraryBook class is the key worker bee in this program. A LibraryBook object keeps track of four pieces of informa- tion and is responsible for computing the overdue charge. Notice that the class is the 7.9 Sample Development 411 program tasks wu23399_ch07.qxd 12/15/06 19:54 Page 411
  • 439. 7.9 Sample Development—continued most appropriate place to perform the computation of the overdue charge because it is where all pieces of information necessary to compute the charge are stored. We will define another class for performing the actual input routines.An instance of this class will input the data,create a LibraryBook object with the input data,and add this object to a BookTracker. As it uses other objects,it will be the main controller of the pro- gram. We will name the class OverdueChecker. We will define it as an instantiable main class.For this program,we will use console input.It is a straightforward exercise to modify the OverdueChecker class to handle input by using JOptionPane or other types of GUI. Now let’s study the second task of displaying the entered book data. To be consistent with the console input, we will use console output. An OverdueChecker will handle the output, but the data to display come from a BookTracker, as it is the one maintaining the collection of LibraryBook objects. The BookTracker class has one method called getBookList. This method returns the book list as a single String value. The OverdueChecker displays the returned string on the standard output window. Notice that the BookTracker class is not programmed to do the output directly,because doing so will reduce its usability.By returning the book list as a String datum,the client of the Book-Tracker class retains the option of using either console output or GUI. This helps to keep the flexibility and increases the usability of a class. For the last task, an OverdueChecker interacts with the user to get the return dates. For each return date entered, it asks BookTracker for the total charge and dis- plays the returned value.The BookTracker in turn asks individual LibraryBook objects for their charges by calling the computeCharge method and computes the sum.This is the reason why we must include the method named computeCharge in the LibraryBook class that computes the overdue charge for a single book.We will discuss the details of this and other requirements in defining the LibraryBook class shortly. Here’s our working design document: 412 Chapter 7 Defining Your Own Classes—Part 2 program classes Design Document: Library OverdueChecker Class Purpose OverdueChecker The top-level control object that manages other objects in the program.This is an instantiable main class,as explained in Section 4.10. BookTracker The predefined helper class that keeps track of library books. LibraryBook An instance of this class represents a single library book. A library book for this program has four properties— title,charge per day,maximum charge,and due date.It is also responsible for computing the overdue charges. Scanner The standard class for handling input routines. wu23399_ch07.qxd 12/15/06 19:54 Page 412
  • 440. Figure 7.11 is the program diagram for this program. We will implement this program in the following five major steps: 1. Define the basic LibraryBook class.Use a test main class to confirm the implementation of the LibraryBook class. 2. Explore the given BookTracker class and integrate it with the LibraryBook class. Modify or extend the LibraryBook class as necessary. 3. Define the top-level OverdueChecker class.Implement the complete input routines.Modify or extend the LibraryBook class as necessary. 4. Complete the LibraryBook class by fully implementing the overdue charge computation. 5. Finalize the program by tying up loose ends. Again, the development strategy we indicate here is one of the possible alterna- tives.We could start from the skeleton main controller class OverdueChecker, as we nor- mally did in the previous sample development examples. For this program, however, we start with the LibraryBook class because of its importance in the program. We want to start from the most important workhorse class.Also,before we implement any elaborate input and output routines, we need to know how the BookTracker class works, and to explore this helper class fully,we need the LibraryBook class. Step 1 Development: The Basic LibraryBook class We begin the development with the basic LibraryBook class.The main purpose in step 1 is to start with the main workhorse class to establish the foundation for the development. Since this class is used by the BookTracker class, we need to find out the compatibility requirements so we won’t define any methods that will violate the compatibility. There 7.9 Sample Development 413 OverdueChecker Scanner BookTracker LibraryBook Figure 7.11 The program diagram for the OverdueChecker program. We will implement the OverdueChecker and LibraryBook classes. The Scanner class is the standard class for console input, and the BookTracker class is the helper class provided for this program. develop- ment steps step 1 design wu23399_ch07.qxd 12/15/06 19:54 Page 413
  • 441. 7.9 Sample Development—continued are two methods used by the BookTracker class,so we need to define them in the Library- Book class.Here are the two required methods and their descriptions: 414 Chapter 7 Defining Your Own Classes—Part 2 Required Methods of LibraryBook public String toString( ) Returns the String representation of itself. This string is used by the BookTracker class to generate a complete book list. public double computeCharge(GregorianCalendar returnDate) Computes and returns the overdue charge for this book, given the return date. The key design task for this step is to identify the data members for storing relevant information and to define their accessors and mutators as appropriate. Also, we will de- sign multiple constructors so an instance of this class can be created in a flexible manner. We define data members for the four pieces of required information for each book as follows: private GregorianCalendar dueDate; private String title; private double chargePerDay; private double maximumCharge; For each of these data members,we will define the corresponding accessors and mutators. We define four constructors. Because the due date is something that must be assigned when a new LibraryBook is created,every constructor requires the due date as its argument.When other optional values are not passed to the constructor, then preset default values are assigned.We define the multiple constructors using the technique we learned in this chapter.The signatures for these constructors are as follows: public LibraryBook(GregorianCalendar dueDate) public LibraryBook(GregorianCalendar dueDate, double chargePerDay) public LibraryBook(GregorianCalendar dueDate, double chargePerDay, double maximumCharge) public LibraryBook(GregorianCalendar dueDate, double chargePerDay, double maximumCharge, String title) wu23399_ch07.qxd 12/15/06 19:54 Page 414
  • 442. We won’t be using the BookTracker class in this step, so we do not have to define the two required methods yet. However, we can use the toString method now to verify the correct operations of constructors and other methods,so we define it now.We use the formatting techniques learned in Section 6.8 to format the string we return from toString. Here’s how we define the toString method: public String toString( ) { return String.format( %-30s $%5.2f $%7.2f %4$tm/%4$td/%4$ty, getTitle(), getChargePerDay(), getMaxCharge(), dueDate.getTime()); } A sample string returned from the method will formatted in the following manner: Introduction to OOP with Java $ 0.75 $ 50.00 07/10/06 Alternatively, we can format the string by using the SimpleDateFormat and DecimalFormat classes. public String toString( ) { String tab = t; SimpleDateFormat sdf = new SimpleDateFormat(MM/dd/yy); DecimalFormat df = new DecimalFormat(0.00); return getTitle() + tab + $ + df.format(getChargePerDay()) + tab + $ + df.format(getMaxCharge()) + tab + sdf.format(dueDate.getTime()); } We are now ready to implement the class. Here’s the step 1 code (minus javadoc and most other comments): 7.9 Sample Development 415 step 1 code /* Chapter 7 Library Overdue Checker Step 1 LibraryBook class File: LibraryBook.java */ wu23399_ch07.qxd 12/15/06 19:54 Page 415
  • 443. 7.9 Sample Development—continued import java.util.*; class LibraryBook { private static final double CHARGE_PER_DAY = 0.50; private static final double MAX_CHARGE = 50.00; private static final String DEFAULT_TITLE = Title unknown; private GregorianCalendar dueDate; private String title; private double chargePerDay; private double maximumCharge; public LibraryBook(GregorianCalendar dueDate) { this(dueDate, CHARGE_PER_DAY); } public LibraryBook(GregorianCalendar dueDate, double chargePerDay) { this(dueDate, chargePerDay, MAX_CHARGE); } public LibraryBook(GregorianCalendar dueDate, double chargePerDay, double maximumCharge) { this(dueDate, chargePerDay, maximumCharge, DEFAULT_TITLE); } public LibraryBook(GregorianCalendar dueDate, double chargePerDay, double maximumCharge, String title) { setDueDate(dueDate); setChargePerDay(chargePerDay); setMaximumCharge(maximumCharge); setTitle(title); } public double getChargePerDay( ) { return chargePerDay; } 416 Chapter 7 Defining Your Own Classes—Part 2 wu23399_ch07.qxd 12/15/06 19:54 Page 416
  • 444. public GregorianCalendar getDueDate( ) { return dueDate; } public double getMaxCharge( ) { return maximumCharge; } public String getTitle( ) { return title; } public void setChargePerDay(double charge) { chargePerDay = charge; } public void setDueDate(GregorianCalendar date) { dueDate = date; } public void setMaximumCharge(double charge) { maximumCharge = charge; } public void setTitle(String title) { this.title = title; } public String toString( ) { return String.format( %-30s $%5.2f $%7.2f %4$tm/%4$td/%4$ty, getTitle(), getChargePerDay(), getMaxCharge(), dueDate.getTime()); } } 7.9 Sample Development 417 The purpose of step 1 testing is to verify we can create LibraryBook objects using different constructors. In addition, we check that the other methods are working cor- rectly,especially the toString method.Here’s one possible test main class: step 1 test /* Introduction to OOP with Java 4th ed., McGraw-Hill File: Step1/Step1Main.java */ wu23399_ch07.qxd 12/15/06 19:54 Page 417
  • 445. 7.9 Sample Development—continued import java.util.*; class Step1Main { public static void main( String[] args ) { //Create three LibraryBook objects and output them GregorianCalendar dueDate; LibraryBook book1, book2, book3, book4; dueDate = new GregorianCalendar(2006, Calendar.MARCH, 14); book1 = new LibraryBook(dueDate); dueDate = new GregorianCalendar(2006, Calendar.FEBRUARY, 13); book2 = new LibraryBook(dueDate, 0.75); book2.setTitle(Introduction to oop with Java); dueDate = new GregorianCalendar(2006, Calendar.JANUARY, 12); book3 = new LibraryBook(dueDate, 1.00, 100.00); book3.setTitle(Java for Smarties); dueDate = new GregorianCalendar(2006, Calendar.JANUARY, 1); book4 = new LibraryBook(dueDate, 1.50, 230.00, Me and My Java); System.out.println(book1.toString()); System.out.println(book2.toString()); System.out.println(book3.toString()); System.out.println(book4.toString()); } } 418 Chapter 7 Defining Your Own Classes—Part 2 Running this program will produce the following output on the standard output window: Title unknown $ 0.50 $ 50.00 03/14/06 Introduction to OOP with Java $ 0.75 $ 50.00 02/13/06 Java for Smarties $ 1.00 $ 100.00 01/12/06 Me and My Java $ 1.50 $ 230.00 01/01/06 wu23399_ch07.qxd 12/15/06 19:55 Page 418
  • 446. 7.9 Sample Development 419 Step 2 Development: Integrate the BookTracker Class into the Program In the second development step, we will bring in the helper BookTracker class into the program. Our main concern in this step is to understand how to interact with a Book- Tracker object correctly and adjust the LibraryBook class, as necessary, to make it com- patible with the BookTracker class. The BookTracker class is actually a fairly straightforward class.You are encouraged to view the source file of the class.To understand the class fully,you need to learn about an ArrayList, a topic covered in Chapter 10.But even without this knowledge,you should be able to understand the majority of the code when you view the source file.We will discuss the implementation of the BookTracker class in Chapter 10. Here’s the class description: step 2 design BookTracker An instance of this class maintains a list of LibraryBook objects. public BookTracker( ) Creates a new instance of the class. public void add( LibraryBook book ) Adds book to the book list it maintains. public double getCharge( ) Returns the total overdue charge for the books in the list.Uses today as the return date. public double getCharge( GregorianCalendar returnDate ) Returns the total overdue charge for the books in the list.The parameter is the date the book is to be returned. public String getList( ) Returns information on all books in the list as a single string. As stated in step 1, the BookTracker class requires two specific methods in the LibraryBook class. We already defined the toString method. Since we will be imple- menting the full computeCharge method in step 4, we define a stub method for this step as public double computeCharge( GregorianCalendar returnDate){ return 1.00; //Stub method for Step 2 } To check our understanding on how to interact with the BookTracker class,we will write a test main class.From this main class,we will create and add multiple book objects to the book tracker and experiment with the getList and getCharge methods. The only change we make to the LibraryBook class is the addition of the stub computeCharge method, so the BookTracker class can be integrated with it. To test step 2 code wu23399_ch07.qxd 12/15/06 19:55 Page 419
  • 447. 7.9 Sample Development—continued the BookTracker class, we define a test main class that checks the cases when the book list is empty and has 20 books.Here’s the test main class: 420 Chapter 7 Defining Your Own Classes—Part 2 /* Introduction to OOP with Java 4th ed., McGraw-Hill File: Step2/Step2Main.java */ import java.util.*; class Step2Main { public static void main( String[] args ) { //Create 20 LibraryBook objects BookTracker bookTracker = new BookTracker(); GregorianCalendar dueDate, returnDate; LibraryBook book; returnDate = new GregorianCalendar(2006, Calendar.MARCH, 15); //Check the error condition System.out.println(Error: No books added. Return code - + bookTracker.getCharge(returnDate)); System.out.println(Output for empty book list:n + bookTracker.getList( )); //Add 20 books System.out.println(nAdding 20 books...n); for (int i = 0; i 20; i++) { dueDate = new GregorianCalendar(2006, Calendar.MARCH, i+1); book = new LibraryBook(dueDate); book.setTitle(Book Number + (i+1)); bookTracker.add(book); } System.out.println(Total Charge: $ + bookTracker.getCharge(returnDate)); System.out.println(n); System.out.println(List: n + bookTracker.getList()); } } wu23399_ch07.qxd 12/15/06 19:55 Page 420
  • 448. We run the test main class and verify that we get the expected results.We will try other variations to increase our confidence before continuing to the next step. Step 3 Development: Define the OverdueChecker Class After the working LibraryBook and BookTracker classes, we are now ready to start im- plementing the top-level controller class.Besides managing a single BookTracker object and multiple LibraryBook objects, an OverdueChecker object’s main responsibility is the handling of input and output routines.As dictated in the problem statement,we have to first input information on books and then repeatedly ask the user for return dates. Expressing this logic in pseudocode,we have GregorianCalendar returnDate; String reply, table; double totalCharge; inputBooks(); //read in all book information table = bookTracker.getList(); System.out.println(table); //try different return dates do { returnDate = read return date ; totalCharge = bookTracker.getCharge(returnDate); displayTotalCharge(totalCharge); reply = prompt the user to continue or not; } while ( reply is yes ); The body of the inputBooks method will include a loop that reads information for one book on each repetition.The method body can be expressed thus: while (isContinue()) { title = readString(Title : ); chargePerDay = readDouble(Charge per day: ); maxCharge = readDouble(Maximum charge: ); dueDate = readDate (Due Date : ); book = createBook(title, chargePerDay, maxCharge, dueDate); bookTracker.add(book); } Notice that there are three types of input data, and we define a method for each type, namely, readDouble, readDate, and readString. These methods read input from a scan- ner (console input) after prompting the user. 7.9 Sample Development 421 step 2 test step 3 design wu23399_ch07.qxd 12/15/06 19:55 Page 421
  • 449. 7.9 Sample Development—continued Code to handle the input of String and double values is straightforward, but the one to handle the input of the date requires some thinking.We need to decide in which format the user can enter the date. For instance, should we prompt for the year, month, and day individually? This may be acceptable if you enter the date once.When you have to input date information many times,this input routine gets tedious.For this application, we will require the user to enter the date correctly as a single string value in the MM/dd/yyyy format. Given a string value in this format, we use a sequence of substring methods to break it down into three pieces—month, day, and year. This operation is similar to the one we used in the Chapter 2 sample application. Then we use the Integer.parseInt method, introduced in Chapter 3, to convert them to int values. From these three int values, we finally create and return a GregorianCalendar object that represents the entered date. After the four values are entered,a new book is created via the createBook method. This method handles the situation when the input value is empty. For example, the user may press only the Enter key if she wants default values for the single-day charge and maximum possible charge. The other methods are straightforward,so we’ll refer you to the complete class list- ing without further explanation.Here’s the instantiable main class OverdueChecker: 422 Chapter 7 Defining Your Own Classes—Part 2 step 3 code /* Chapter 7 Library Overdue Checker Step 3 Implement the Main Controller */ import java.util.*; class OverdueChecker { private static enum Response {YES, NO} private static final String DATE_SEPARATOR = /; private Scanner scanner; private BookTracker bookTracker; //------------------------------------------------- // Constructors //------------------------------------------------- public OverdueChecker() { scanner = new Scanner(System.in); wu23399_ch07.qxd 12/15/06 19:55 Page 422
  • 450. scanner.useDelimiter(System.getProperty(line.separator)); bookTracker = new BookTracker(); } //------------------------------------------------- // Main Method //------------------------------------------------- public static void main(String[] args) { OverdueChecker checker = new OverdueChecker(); checker.start(); } //------------------------------------------------- // Public Methods //------------------------------------------------- public void start( ) { GregorianCalendar returnDate; String table; double charge; Response response; inputBooks(); table = bookTracker.getList(); System.out.println(table); System.out.println(nNow check the over due charges...n); //try different return dates do { //read return date returnDate = readDate(nReturn Date: ); charge = bookTracker.getCharge(returnDate); displayTotalCharge(charge); response = prompt(nRun Again (yes/no)? ); } while (response == Response.YES); System.out.println( nnThank you for using Library Overdue Checker); } //------------------------------------------------- // Private Methods //------------------------------------------------- 7.9 Sample Development 423 wu23399_ch07.qxd 12/15/06 19:55 Page 423
  • 451. 7.9 Sample Development—continued private LibraryBook createBook(String title, double chargePerDay, double maxCharge, GregorianCalendar dueDate) { if (dueDate == null) { dueDate = new GregorianCalendar(); //set today as due date } LibraryBook book = new LibraryBook(dueDate); if (title.length() 0) { book.setTitle(title); } if (chargePerDay 0.0) { book.setChargePerDay(chargePerDay); } if (maxCharge 0.0) { book.setMaximumCharge(maxCharge); } return book; } private void display(String text) { System.out.print(text); } private void displayTotalCharge(double charge) { System.out.format(nTOTAL CHARGE:t $%8.2f, charge); } private void inputBooks( ) { double chargePerDay, maxCharge; String title; GregorianCalendar dueDate; LibraryBook book; //Keeps on reading input from a console //until stopped by the end user while (isContinue()) { System.out.println(n); title = readString(Title : ); chargePerDay = readDouble(Charge per day: ); 424 Chapter 7 Defining Your Own Classes—Part 2 wu23399_ch07.qxd 12/15/06 19:55 Page 424
  • 452. maxCharge = readDouble(Maximum charge: ); dueDate = readDate (Due Date : ); book = createBook(title, chargePerDay, maxCharge, dueDate); bookTracker.add(book); } } private boolean isContinue( ) { Response response = prompt(nMore books to enter (y/n)?); return (response == Response.YES); } private Response prompt(String question) { String input; Response response = Response.NO; System.out.print(question + (Yes - y; No - n): ); input = scanner.next(); if (input.equals(Y) || input.equals(y)) { response = Response.YES; } return response; } private double readDouble(String prompt) { display(prompt); return scanner.nextDouble(); } private GregorianCalendar readDate( String prompt) { GregorianCalendar cal; String yearStr, monthStr, dayStr, line; int sep1, sep2; display(prompt); line = scanner.next(); 7.9 Sample Development 425 wu23399_ch07.qxd 12/15/06 19:55 Page 425
  • 453. 7.9 Sample Development—continued if (line.length() == 0) { cal = null; } else { sep1 = line.indexOf(DATE_SEPARATOR); sep2 = line.lastIndexOf(DATE_SEPARATOR); monthStr = line.substring(0, sep1); dayStr = line.substring(sep1 + 1, sep2); yearStr = line.substring(sep2 + 1, line.length()); cal = new GregorianCalendar(Integer.parseInt(yearStr), Integer.parseInt(monthStr)-1, Integer.parseInt(dayStr)); } return cal; } private String readString(String prompt) { display(prompt); return scanner.next(); } } 426 Chapter 7 Defining Your Own Classes—Part 2 step 3 test Now we run the program multiple times,trying different input types and values.We also confirm that all control loops are implemented and working correctly. At this point, the code to compute the overdue charge is still a stub, so we will always get the same overdue charge for the same number of books.After we verify that everything is working as expected,we proceed to the next step. Step 4 Development: Compute the Overdue Charge In step 4, we complete the stub method that computes the overdue charge in the LibraryBook class. We have two GregorianCalendar objects for the due date and the return date.We first need to find out the number of days between the two.We then mul- tiply this number by the amount of charge per day to derive the total overdue charge. If this amount is more than the maximum possible charge, then the total charge is reset to this maximum value.Also,we need to check for the situation in which the return date has not passed the due date.The logic of this process is a simple computation once we find out the number of days between the two dates.So,how can we find it? step 4 design wu23399_ch07.qxd 12/15/06 19:55 Page 426
  • 454. Reviewing the GregorianCalendar class, we see the get method can be used to retrieve different pieces of date information, such as year, month, and day. Using the method, we can get the month, day, and year information for two dates and compare these values. It may sound easy, but things can get tricky very quickly. Complexity arises from the facts that not every month has the same number of days and that the number of days for February can vary from year to year. This approach is doable, but not recom- mended. When we explore the class further, we notice there’s another method, namely get- Time, that returns a Date object.In Chapter 6,we used this Date class to compute the ex- ecution time of a loop by finding the difference between the start and end times. We can apply the same technique here. But instead of using the getTime method, we can actually use the getTimeInMillis method and bypass the Date class altogether. The getTimeInMillis method returns the time elasped since the epoch to the date in mil- liseconds.By subtracting this since-the-epoch milliseconds value of the due date from the same of the return date, we can find the difference between the two. If the difference is negative, then it’s not past due, so there’s no charge. If the difference is positive, then we convert the milliseconds to the equivalent number of days and multiply it by the per-day charge to compute the total charge.Here’s a simple way to do the conversion: private static final double MILLISEC_TO_DAY = 1.0 / 1000 / 60 / 60 / 24; ... dayCnt = millisec * MILLISEC_TO_DAY; We will adopt the second approach. Here’s the final computeCharge method of the LibraryBook class: public double computeCharge(GregorianCalendar returnDate){ double charge = 0.0; long dueTime = dueDate.getTimeInMillis(); long returnTime = returnDate.getTimeInMillis(); long diff = returnTime - dueTime; if (diff 0) { charge = chargePerDay * diff * MILLISEC_TO_DAY; if (charge maximumCharge) { charge = maximumCharge; } } return charge; } We run the program mutiple times again,possibly using the same set of input data. We enter different input variations to try out all possible cases for the computeCharge 7.9 Sample Development 427 step 4 code design alternative 1 design al- ternative 2 step 4 test wu23399_ch07.qxd 12/15/06 19:55 Page 427
  • 455. 7.9 Sample Development—continued method.Try cases such as the return date and due date are the same, the return date oc- curs before the due date, the charge is beyond the maximum, and so forth. After we ver- ify the program,we move on to the next step. Step 5 Development:Tying Up the Loose Ends and Future Extensions As always, we will perform a critical review of the program, looking for any unfinished method, inconsistency or error in the methods, unclear or missing comments, and so forth.We should also not forget to improve the program for cleaner code and better read- ability.This is especially true for the input routines.Are all the possible cases handled? Are the input routines easy to use? Will it be better if we allow different formats for entering the date information? We stated at the beginning of this section that it would be a better program if it warned the user, say, by popping a warning window or ringing an alarm, when the due date was approaching. Using this extended program, we enter the book data at the time we check out the book from the library.The program will store the entered information in a file, so we don’t have to reenter the same data whenever we want to find out the total overdue charge. We can execute the program daily and be warned about the looming due dates. We can still run the program to find out the charges for the overdue books. Techniques necessary to implement such an extended program are covered in the later chapters of this book. 428 Chapter 7 Defining Your Own Classes—Part 2 program review possible extensions S u m m a r y • When a method returns an object, it is actually returning a reference to this object. • The reserved word this is used to refer to a receiving object of a message from within this object’s method. • A class may include multiple methods with the same name as long as their signatures are different. The signature of a method refers to the name of the method and the number and data types of its parameters. They are called overloaded methods. • A class may include multiple constructors as long as their signatures are different. They are called overloaded constructors. • A constructor can call another constructor of the same class using the reserved word this. • Class variables and class methods are declared by using the reserved word static. • Class methods can access only the class variables and the class constants. wu23399_ch07.qxd 12/15/06 19:55 Page 428
  • 456. • Instance methods can access all types of data members (i.e., both class and instance components). • Arguments are passed to the methods by using the call-by-value scheme in which the value of an argument is passed. The value is the actual data in the case of a primitive data type and a reference to an object in the case of a reference data type. • Programmer-defined classes can be grouped into a programmer-defined package. • The javadoc comment is the third style of comments used in Java. From the javadoc comments in a class, a tool can generate its documentation in the HTML format. Exercises 429 K e y C o n c e p t s E x e r c i s e s returning objects from methods self referencing pointer (this) overloaded methods method signatures multiple constructors copy constructors static initializers call-by-value scheme programmer-defined packages javadoc comments 1. Consider the following classes. class Cat { private String name; private Breed breed; private double weight; public Cat(String name, Breed breed, double weight){ this.name = name; this.breed = breed; this.weight = weight; } public Breed getBreed() { return breed; } public double getWeight() { return weight; } //other accessors and mutators . . . } wu23399_ch07.qxd 12/15/06 19:55 Page 429
  • 457. class Breed { private String name; private double averageWgt; //in lbs. public Breed(String name, double averageWgt){ this.name = name; this.averageWgt = averageWgt; } public double getWeight( ) { return averageWgt; } //other accessors and mutators . . . } Identify the invalid statements in the following main class. For each invalid statement, state why it is invalid. class Q1Main { public static void main(String[] args ) { Breed persian = new Breed(Persian, 10.0); Cat chacha = new Cat(Cha Cha, persian, 12.0); Cat bombom = new Cat(Bom Bom, mix, 10.0); Cat puffpuff = new Cat(Puff Puff, chacha, 9.0); double diff = chacha.getWeight() - persian.getWeight(); System.out.println( puffpuff.getBreed().getWeight()); } } 2. Given the Cat and Breed classes from Exercise 1, what will be the output from the following code? class Q2Main { public static void main(String[] args) { Cat myCat = new Cat(winky, new Breed(mix, 10.5), 9.5); System.out.println(myCat.getWeight()); System.out.println(myCat.getBreed().getWeight()); } } 430 Chapter 7 Defining Your Own Classes—Part 2 wu23399_ch07.qxd 12/15/06 19:55 Page 430
  • 458. 3. Given the Fraction class from Section 7.8, draw the state-of-memory diagram at the point immediately after the last statement is executed. Fraction f1, f2, f3; f1 = new Fraction(3, 8); f2 = new Fraction(2, 3); f3 = f1.add(f2); 4. Consider the following class. class Dog { . . . private double weight; . . . public boolean isBiggerThan(Dog buddy) { return this.getWeight() buddy.getWeight(); } public double getWeight() { return weight; } . . . } For each of the following codes, complete the state-of-memory diagram by filling in the arrows for this and buddy. a. Dog tuffy = new Dog(...); Dog puffy = new Dog(...); puffy.isBiggerThan(tuffy); tuffy puffy :Dog :Dog this buddy Exercises 431 wu23399_ch07.qxd 12/15/06 19:55 Page 431
  • 459. b. Dog tuffy = new Dog(...); Dog puffy = new Dog(...); tuffy.isBiggerThan(puffy); 5. Complete the following constructor. class Student { private String name; private int age; private Address address; public Student(String name, int age, Address address){ //assign passed values to the data members } 6. Which of the following groups of overloaded constructors are valid? a. public Cat(int age) { ... } public Cat(double wgt) { ... } b. public Dog(String name, double weight) { ... } public Dog(String name, double height) { ... } c. public Dog(String name, double weight) { ... } public Dog(double weight, String name) { ... } d. public Cat(String name) { ... } public Cat(String name, double weight) { ... } public Cat(double weight) { ... } 7. Which of the following groups of overloaded methods are valid? a. public void compute(int num) { ... } public int compute(double num) { ... } b. public void move(double length) { ... } public void move( ) { ... } tuffy puffy :Dog :Dog this buddy 432 Chapter 7 Defining Your Own Classes—Part 2 wu23399_ch07.qxd 12/15/06 19:55 Page 432
  • 460. c. public int adjust(double amount) { ... } public void adjust(double amount, double charge) { ... } d. public void doWork( ) { ... } public void doWork(String name) { ... } public int doWork(double num) { ... } 8. Complete the first four constructors of the following class. Each of the four constructors calls the fifth one by using the reserved word this. class Cat { private static final String DEFAULT_NAME = No name; private static final int DEFAULT_HGT = 6; private static final double DEFAULT_WGT = 10.0; private String name; private int height; private double weight; public Cat( ) { //assign defaults to all data members } public Cat(String name) { //assign the passed name to the data member //use defaults for height and weight } public Cat(String name, int height) { //assign passed values to name and height //use default for weight } public Cat(String name, double weight) { //assign passed values to name and weight //use default for height } public Cat(String name, int height, double weight){ this.name = name; this.height = height; this.weight = weight; } ... } 9. Define a class method (static) named compare to the Fraction class. The compare method accepts two Fraction objects f1 and f2. The method returns -1 if f1 is less than f2 0 if f1 is equal to f2 +1 if f1 is greater than f2 Exercises 433 wu23399_ch07.qxd 12/15/06 19:55 Page 433
  • 461. 10. Rewrite the compare method from Exercise 9 by changing it to an instance method. This method accepts a Fraction object and compares it to the receiving object. The method is declared as follows: public int compare(Fraction frac) { //compare the Fraction objects this and frac //return the result of comparison } 11. Discuss the pros and cons of the compare methods from Exercise 8 and Exercise 9. 12. Consider the following class. class Modifier { public static change(int x, int y){ x = x - 10; y = y + 10; } } What will be an output from the following code? int x = 40; int y = 20; Modifier.change(x,y); System.out.println(x = + x); System.out.println(y = + y); 13. Modify the following class to make it a part of the package named myutil. In addition to adjusting the source file, what are the steps you need to take so that the class becomes usable/accessible from other classes that are outside of this myutil package? class Person { private String name; public Person( ) { name = Unknown; } public String getName() { return name; } public void setName(String name) { this.name = name; } } 14. (Optional) Although we have not discussed the internal workings of the BookTracker class, it is not too difficult to realize the portion that handles the 434 Chapter 7 Defining Your Own Classes—Part 2 wu23399_ch07.qxd 12/15/06 19:55 Page 434
  • 462. generation of book list. Define a new method called getListWithCharge based on the getList method. Generate a book list as the getList method does, but include the overdue charge for each book also. 15. Design a class that keeps track of a student’s food purchases at the campus cafeteria. A meal card is assigned to an individual student. When a meal card is first issued, the balance is set to the number of points. If the student does not specify the number of points, then the initial balance is set to 100 points. Points assigned to each food item are a whole number. A student can purchase additional points at any time during a semester. Every time food items are bought, points are deducted from the balance. If the balance becomes negative, the purchase of food items is not allowed. There is obviously more than one way to implement the MealCard class. Any design that supports the key functionalities is acceptable. Development Exercises For the following exercises, use the incremental development methodology to implement the program. For each exercise, identify the program tasks, create a design document with class descriptions, and draw the program diagram. Map out the development steps at the start. Present any design alternatives and justify your selection. Be sure to perform adequate testing at the end of each development step. 16. Write an application that plays the game of Fermi. Generate three distinct random digits between 0 and 9. These digits are assigned to positions 1, 2, and 3. The goal of the game is for the player to guess the digits in three positions correctly in the least number of tries. For each guess, the player provides three digits for positions 1, 2, and 3. The program replies with a hint consisting of Fermi, Pico, or Nano. If the digit guessed for a given position is correct, then the reply is Fermi. If the digit guessed for a given position is in a different position, the reply is Pico. If the digit guessed for a given position does not match any of the three digits, then the reply is Nano. Here are sample replies for the three secret digits 6, 5, and 8 at positions 1, 2, and 3, respectively: Guess Hint Explanation 1 2 5 Nano Nano Pico The value 5 matches but at the wrong position. 8 5 3 Pico Fermi Nano The value 5 matches at the correct position. The value 8 matches but at the wrong position. 5 8 6 Pico Pico Pico All match at the wrong positions. Notice that if the hints like the above are given, the player can tell which number did not match. For example, given the hint for the second guess, we Exercises 435 wu23399_ch07.qxd 12/15/06 19:55 Page 435
  • 463. can tell that 3 is not one of the secret numbers. To avoid this, provide hints in a random order or in alphabetical order (e.g., it will be Fermi Nano Pico instead of Pico Fermi Nano for the second reply). Play games repeatedly until the player wants to quit. After each game, display the number of guesses made. Use javadoc comments to document the classes you design for this application. 17. Write an application that teaches children fraction arithmetic. For each training session, randomly generate 10 questions involving addition, subtraction, division, and multiplication of two fractions. At the beginning of each session, the user has the option of specifying the time limit for answering the questions. If the time limit is not specified, then use 30 s as a default time limit. After you pose a question, wait until the user answers the question. Award points based on the following rules: Answer Time Points Correct Under limit 10 Correct Over limit 6 Wrong Under limit 3 Wrong Over limit 0 After one session is over, use the console output to display the grade distribution and the total points in the following manner: Under Over Time Limit Time Limit Correct Answers 4 3 Wrong Answers 2 1 TOTAL POINTS: 64 (40 + 18 + 6 + 0) After one session is over, give the user the option to play another session. 436 Chapter 7 Defining Your Own Classes—Part 2 wu23399_ch07.qxd 12/15/06 19:55 Page 436
  • 464. Exceptions and Assertions O b j e c t i v e s After you have read and studied this chapter,you should be able to • Improve the reliability of code by incorporating exception-handling and assertion mechanisms. • Write methods that propagate exceptions. • Implement the try-catch blocks for catching and handling the thrown exceptions. • Write programmer-defined exception classes. • Distinguish between the checked and unchecked,or runtime,exceptions. • Use assertions in methods to increase the chance of detecting bugs during the development. 437 8 wu23399_ch08.qxd 12/15/06 19:57 Page 437
  • 465. hen someone says his or her program is reliable, what do we expect from the program? The majority of people would probably reply correctness as the most important criterion in determining the reliability of a program. When a program is claimed to be reliable, we certainly expect the program will produce correct results for all valid input. It is hardly a reliable program if it produces correct results only for some input values. As we all know by now, writing a correct program is easier said than done. If we are not diligent and careful enough, we can easily introduce bugs in our programs. And often we fail to eradicate them. A mechanism called an assertion can be used to improve the likelihood of catching logical errors during the development. We will introduce assertions in this chapter and show how to use them effectively in our programs. Program correctness guarantees correct results for all valid input. But what happens when the input is invalid? Another important criterion of program reliability is the robustness, which measures how well the program runs under various conditions. If a program crashes too easily when a wrong type of argu- ment is passed to a method or an invalid input value is entered, we cannot say the program is very reliable. A mechanism called exception handling can be used to improve the program’s robustness. In this chapter, we will describe how to code this exception-handling mechanism in Java to improve the program’s robustness. 8.1 Catching Exceptions In Chapters 5 and 6 we presented two types of control flows: selection control and repetition control. Using these control structures, we alter the default sequential flow of control. We use a selection control to select and execute one block of code out of many choices, and we use a repetition control to execute a block of code repeatedly until certain conditions are met. The exception-handling mechanism can be viewed as another form of control structure. An exception represents an error condition that can occur during the normal course of program execution. When an exception occurs, the normal sequence of flow is terminated and the exception-handling routine is executed. When an exception occurs, we say an exception is thrown. When the matching exception-handling code is executed, we say the thrown exception is caught. By using exception-handling routines judiciously in our code, we can increase its robustness. In this section, we will show how the thrown exceptions can be caught and processed. We have been dealing with exceptions all along. For example, consider this code: Scanner scanner = new Scanner(System.in); System.out.print(Enter integer: ); int number = scanner.nextInt(); 438 Chapter 8 Exceptions and Assertions I n t r o d u c t i o n W assertion exception handling exception wu23399_ch08.qxd 12/15/06 19:57 Page 438
  • 466. What would happen if we entered, say, abc123, an input value that is not an int? We would get an error message like this: Exception in thread main java.util.InputMismatchException at java.util.Scanner.throwFor(Scanner.java:819) at java.util.Scanner.next(Scanner.java:1431) at java.util.Scanner.nextInt(Scanner.java:2040) at java.util.Scanner.nextInt(Scanner.java:2000) at Ch8Sample1.main(Ch8Sample1.java:35) This error message indicates the system has caught an exception called the Input- MismatchException, an error that occurs when we try to convert a string that cannot be converted to a numerical value. Up until now, we have let the system handle the thrown exceptions. However, when we let the system handle the exceptions, a single thrown exception most likely will result in erroneous results or a program termination. Instead of depending on the system for exception handling, we can increase the program’s reliability and robustness if we catch the exceptions our- selves by including error recovery routines in our program. Let’s begin with a short program to illustrate the exception-handling mechanism. We will define a service class that supports a method to input a person’s age. This class is mainly for the illustrative purpose of introducing the exceptionhandling concept.We first define it without exception handling and then improve it gradually by adding exception-handling features. Because we will be defining many different versions of the class, we will name them AgeInputVer1, AgeInputVer2, and so forth. Here’s the AgeInputVer1 class without exception handling: 8.1 Catching Exceptions 439 /* Chapter 8 Sample Class: Class to input age File: AgeInputVer1.java */ import java.util.*; class AgeInputVer1 { private static final String DEFAULT_MESSAGE = Your age: ; private Scanner scanner; public AgeInputVer1( ) { scanner = new Scanner(System.in); } public int getAge() { return getAge(DEFAULT_MESSAGE); } wu23399_ch08.qxd 12/15/06 19:57 Page 439
  • 467. Using this service class, we can write a program that gets a person’s age and replies with the year in which the person was born. Notice the program takes into con- sideration whether the person already had a birthday this year. Here’s the program: 440 Chapter 8 Exceptions and Assertions public int getAge(String prompt) { System.out.print(prompt); int age = scanner.nextInt(); return age; } } /* Chapter 8 Sample Program: Input a person's age File: Ch8AgeInputMain.java */ import java.util.*; class Ch8AgeInputMain { public static void main(String[] args) { GregorianCalendar today; int age, thisYear, bornYr; String answer; Scanner scanner = new Scanner(System.in); AgeInputVer1 input = new AgeInputVer1( ); age = input.getAge(How old are you? ); today = new GregorianCalendar( ); thisYear = today.get(Calendar.YEAR); bornYr = thisYear - age; System.out.print(Already had your birthday this year? (Y or N)); answer = scanner.next(); if (answer.equals(N) || answer.equals(n) ) { bornYr--; } System.out.println(nYou are born in + bornYr); } } wu23399_ch08.qxd 12/15/06 19:57 Page 440
  • 468. The program works fine as long as valid input is entered. But what happens if the user spells out the age, say, nine instead of 9? An input mismatch exception is thrown because the input value nine cannot be converted to an integer by using the parseInt method. With the current implementation, the system will handle the thrown exception by displaying the error message Exception in thread main java.util.InputMismatchException at java.util.Scanner.throwFor(Scanner.java:819) at java.util.Scanner.next(Scanner.java:1431) at java.util.Scanner.nextInt(Scanner.java:2040) at java.util.Scanner.nextInt(Scanner.java:2000) at AgeInputVer1.getAge(AgeInputVer1.java:48) at Ch8AgeInputMain.main(Ch8AgeInputMain.java:30) and terminating the program. It would be a much better program if we could handle the thrown exception ourselves. Let’s modify the getAge method so that it will loop until a valid input that can be converted to an integer is entered. To do this, we need to wrap the statements that can potentially throw an exception with the try-catch control statement. In this example, there’s only one statement that can potentially throw an exception, namely, age = scanner.nextInt(); We put this statement inside the try block and the statements we want to be executed in response to the thrown exception in the matching catch block. If we just want to display an error message when the exception is thrown, then we can write the try-catch statement as follows: System.out.print(prompt); try { age = scanner.nextInt(); } catch (InputMismatchException e) { System.out.println( Invalid Entry. Please enter digits only.); } Statements in the try block are executed in sequence. When one of the state- ments throws an exception, then control is passed to the matching catch block and statements inside the catch block are executed. The execution next continues to the statement that follows this try block statement, ignoring any remaining statements in the try block. If no statements in the try block throw an exception, then the catch block is ignored and execution continues with the statement that follows this 8.1 Catching Exceptions 441 try-catch A statement that could throw an exception The type of exception to be caught wu23399_ch08.qxd 12/15/06 19:57 Page 441
  • 469. try-catch statement. Figure 8.1 shows the two possible control flows: one when an exception is thrown and another when no exceptions are thrown. In the sample code, we have only one statement in the try block. If the input statement does not throw an exception, then we want to exit from the method and return the integer. If there’s an exception, we display an error message inside the catch block, and repeat the input routine. To accomplish this repetition, we will put the whole try-catch statement inside a loop: public int getAge(String prompt) { int age; boolean keepGoing = true; while (keepGoing) { System.out.print(prompt); try { age = scanner.nextInt(); keepGoing = false; } catch (InputMismatchException e) { scanner.next(); //remove the leftover garbage //from the input buffer System.out.println( 442 Chapter 8 Exceptions and Assertions Assume t–stmt–3 throws an exception. Exception try { t–stmt–1 t–stmt–2 t–stmt–3 t–stmt–4 ... t-stmt-n } catch (Exception e) { c–stmt–1 ... c–stmt–n } next stmt This part is skipped. No exception try { t–stmt–1 t–stmt–2 t–stmt–3 t–stmt–4 ... t–stmt n } catch (Exception e) { c–stmt–1 ... c–stmt–n } next stmt Figure 8.1 Two possible control flows of the try-catch statement with one catch block.Assume t-stmt-3 throws an exception. This statement is executed only if no exception is thrown. This will remove “garbage”left in the input buffer. wu23399_ch08.qxd 12/15/06 19:57 Page 442
  • 470. Invalid Entry.Please enter digits only.); } } return age; } Notice the first statement scanner.next(); inside the catch block. It is used to remove any data that remain in the input buffer. When an exception is thrown, an input value that has caused an exception still remains in the input buffer. We need to remove this “garbage” from the input buffer, so we can process the next input value. If we don’t include this statement, the code will result in an infinite loop because the nextInt method continues to process the same invalid input. We can get rid of the boolean variable by rewriting the statement as while (true) { System.out.print(prompt); try { age = scanner.nextInt(); return age; } catch (InputMismatchException e) { scanner.next(); //remove the leftover garbage //from the input buffer System.out.println( Invalid Entry. Please enter digits only.); } } The improved class with the exception-handling getAge method is named AgeInputVer2. There are many types of exceptions the system can throw, and we must specify which exception we are catching in the catch block’s parameter list (there can be exactly one exception in the list). In the sample code, we are catching the input mismatch exception, and the parameter e represents an instance of the InputMis- matchException class. In Java an exception is represented as an instance of the Throwable class or its subclasses. The Throwable class has two subclasses, Error and Exception. The Error class and its subclasses represent serious problems that should not be caught by ordinary applications, while the Exception class and its subclasses represent error conditions that should be caught. So for all practical purposes, we are only interested in the Exception class and its subclasses in our program. Later in the chapter we will learn how to define our own exception classes. We will declare these programmer-defined exception classes as subclasses of the Exception class. 8.1 Catching Exceptions 443 wu23399_ch08.qxd 12/15/06 19:57 Page 443
  • 471. There are two methods defined in the Throwable class that we can call to get some information about the thrown exception: getMessage and printStackTrace. We can call these methods inside the catch block as follows: try { age = scanner.nextInt(); return age; } catch (InputMismatchException e) { scanner.next (); //remove the leftover garbage //from the input buffer System.out.println(e.getMessage()); e.printStackTrace(); } With this modified code, if we enter ten as an input, then we will receive the following output: null java.util.InputMismatchException at java.util.Scanner.throwFor(Scanner.java:819) at java.util.Scanner.next(Scanner.java:1431) at java.util.Scanner.nextInt(Scanner.java:2040) at java.util.Scanner.nextInt(Scanner.java:2000) at AgeInputVer2.getAge(AgeInputVer2.java:54) at Ch8AgeInputMain.main(Ch8AgeInputMain.java:30) Notice that the result we see from the printStackTrace method is the one we saw when the system handled the thrown exception. The stack trace shows the sequence of calls made from the main method of the main class to the method that throws the exception. 444 Chapter 8 Exceptions and Assertions getMessage printStackTrace 1. What will be displayed on the console window when the following code is executed and the user enters abc123 and 14? Scanner scanner = new Scanner(System.in); try { int num1 = scanner.nextInt(); System.out.println(Input 1 accepted); int num2 = scanner.nextInt(); System.out.println(Input 2 accepted); } catch (InputMismatchException e) { System.out.println(Invalid Entry); } wu23399_ch08.qxd 12/15/06 19:57 Page 444
  • 472. 2. What is wrong with the following code? It attempts to loop until the valid input is entered. Scanner scanner = new Scanner(System.in); try { while (true) { System.out.print(Enter input: ); int num = scanner.nextInt(); } } catch (InputMismatchException e) { scanner.next(); System.out.println(Invalid Entry); } 8.2 Throwing Exceptions and Multiple catch Blocks Compared to the original AgeInputVer1 class, the AgeInputVer2 class is more robust because the program does not terminate abruptly when an invalid value is entered. However, the improved class is not robust enough yet. There is still room for im- provements. For example, the current implementation accepts invalid negative inte- gers. Since negative age is not possible, let’s improve the code by disallowing the input of negative integers. Notice that a negative integer is an integer, so the nextInt method will not throw an exception. We will define the third class, AgeInputVer3, to throw (and catch) an exception when the invalid input of a negative integer is detected. Here’s the while loop of the modified getAge method of the AgeInputVer3 class: while (true) { System.out.print(prompt); try { age = scanner.nextInt(); if (age 0) { throw new Exception(Negative age is invalid); } return age; //input okay so return the value exit } catch (InputMismatchException e) { scanner.next(); System.out.println(Input is invalid.n + Please enter digits only); } catch (Exception e) { System.out.println(Error: + e.getMessage()); } } 8.2 Throwing Exceptions and Multiple catch Blocks 445 Throws an excep- tion when age is a negative integer. The thrown exception is caught by this catch block. wu23399_ch08.qxd 12/15/06 19:57 Page 445
  • 473. An exception is thrown by using the throw statement. Its syntax is throw a throwable object where a throwable object is an instance of the Throwable class or its subclasses. As mentioned earlier, in common applications, it will be an instance of the Exception class or its subclasses. In the sample code, we threw an instance of the Exception class. When we create an instance of the Exception class, we can pass the string that describes the error. The thrown exception is caught by the corresponding catch block, and this error message is displayed. Notice the multiple catch blocks in the sample code. When there are multi- ple catch blocks in a try-catch statement, they are checked in sequence, and because the exception classes form an inheritance hierarchy, it is important to check the more specialized exception classes before the more general exception classes. For example, if we reverse the order of the catch blocks to try { ... } catch (Exception e) { ... } catch (InputMismatchException e) { ... } then the second catch block will never be executed because any exception object that is an instance of Exception or its subclasses will match the first catch block. When an exception is thrown, a matching catch block is executed and all other catch blocks are ignored. This is similar to the switch statement with break at the end of each case block. The execution continues to the next statement that follows the trycatch statement. When no exception is thrown, all catch blocks are ignored and the execution continues to the next statement. Figure 8.2 illustrates the control flow of the try-catch statement with multiple catch blocks. 446 Chapter 8 Exceptions and Assertions List the catch blocks in the order of specialized to more general exception classes. At most one catch block is executed,and all other catch blocks are ignored. The sample code given at the beginning of this section illustrates how an ex- ception can be thrown and caught by the matching catch block. Instead of catching the thrown exception immediately, it is possible to let others handle the exception. This can be achieved by not including a matching catch block. We assume in Figure 8.2 that one of the catch blocks will match the thrown exception, but it is not a require- ment. It is possible that none of the catch blocks matches the thrown exception. wu23399_ch08.qxd 12/15/06 19:57 Page 446
  • 474. If there is no matching catch block, then the system will search down the stack trace for a method with a matching catch block. If none is found, then the system will handle the thrown exception. We will explain this traversing of the stack trace in greater detail in Section 8.3. If there is a block of code that needs to be executed regardless of whether an exception is thrown, then we use the reserved word finally. Consider this code. try { num = scanner.nextInt(); if (num 100) { throw new Exception(Out of bound); } } catch (InputMismatchException e) { scanner.next(); System.out.println(Not an integer); } catch (Exception e) { System.out.println(Error: + e.getMessage()); } finally { System.out.println(DONE); } 8.2 Throwing Exceptions and Multiple catch Blocks 447 Assume t–stmt–3 throws an exception and catch–block–3 is the matching catch block. Exception try { t–stmt–1 t–stmt–2 t–stmt–3 t–stmt–4 ... t–stmt–n } catch–block–1 catch–block–2 catch–block–3 catch–block–4 ... catch–block–n next stmt try { t–stmt–1 t–stmt–2 t–stmt–3 t–stmt–4 ... t–stmt–n } catch–block–1 catch–block–2 catch–block–3 catch–block–4 ... catch–block–n next stmt No exception Skipped portion Figure 8.2 Two possible control flows of the try-catch statement with multiple catch blocks.Assume t-stmt-3 throws an exception and catch-block-3 is the matching catch block. wu23399_ch08.qxd 12/15/06 19:57 Page 447
  • 475. If there is no error in input, then no exception is thrown and the output will be DONE If there is an error in input, one of the two exceptions is thrown and the output will be Not an integer DONE or Error: Out of bound DONE The example shows that the finally block is always executed. This feature is use- ful in a situation where we need to execute some cleanup code after the try-catch state- ment. For example, suppose we open a communication channel from our Java program to a remote Web server to exchange data. If the data exchange is successfully com- pleted in the try block, then we close the communication channel and finish the opera- tion. If the data exchange is interrupted for some reason, an exception is thrown and the operation is aborted. In this case also, we need to close the communication channel, because leaving the channel open by one application blocks other applications from using it. Closing a channel is much like hanging up the phone. The code to close the communication channel should therefore be placed in the finally block. Figure 8.3 shows two possible control flows for the try-catch statement with the finally clause. 448 Chapter 8 Exceptions and Assertions Assume t–stmt–i throws an exception and catch–block–i is the matching catch block. Exception try { t–stmt–1 ... t–stmt–i ... t–stmt–n } catch–block–1 ... catch–block–i ... catch–block–n finally { ... } next statement try { t–stmt–1 ... t–stmt–i ... t–stmt–n } catch–block–1 ... catch–block–i ... catch–block–n finally { ... } next statement No exception Skipped portion Figure 8.3 Two possible control flows of the try-catch statement with multiple catch blocks and the finally block.The finally block is always executed. wu23399_ch08.qxd 12/15/06 19:57 Page 448
  • 476. Note that even if there’s a return statement inside the try block, the finally block is executed. When the return statement is encountered in the try block, statements in the finally block are executed before actually returning from the method. 8.2 Throwing Exceptions and Multiple catch Blocks 449 1. What’s wrong with the following code? Identify all errors. Scanner scanner = new Scanner(System.in); try { int num = scanner.nextInt(); if (num 100) { catch new Exception(Out of bound); } } catch (InputMismatchException e) { System.out.println(Invalid Entry); } finally(Exception e) { System.out.println(DONE); } 2. Determine the output of the following code when the input a12 is entered. Scanner scanner = new Scanner(System.in); try { int num = scanner.nextInt(); if (num 0) { throw new Exception(No negative); } } catch (InputMismatchException e) { System.out.println(Invalid Entry); } catch (Exception e) { System.out.println(Error: + e.getMessage()); } finally { System.out.println(DONE); } wu23399_ch08.qxd 12/15/06 19:57 Page 449
  • 477. 3. Determine the output of the following code when the input a12 is entered. Scanner scanner = new Scanner(System.in); try { int num = scanner.nextInt(); if (num 0) { throw new Exception(No negative); } } catch (Exception e) { System.out.println(Error: + e.getMessage()); } catch (InputMismatchException e) { System.out.println(Invalid Entry); } 8.3 Propagating Exceptions In Section 8.2 we introduced the possibility of no catch block matching the thrown exception, but we did not explain exactly how the system handles such a case. We stated only briefly that the system will search down the stack trace for a method with a matching catch block, and if no matching catch block is found, the system will handle the thrown exception. We now describe this mechanism in detail. To present a precise description, we start with some definitions. When a method may throw an exception, either directly by including a throw statement or indirectly by calling a method that throws an exception, we call the method an exception thrower. Every exception thrower must be one of the two types: catcher or propagator. An exception catcher is an exception thrower that includes a matching catch block for the thrown exception, while an exception propagator does not. For example, the getAge method of the AgeInputVer1 class is an exception propagator, while the getAge method of the AgeInputVer2 class is an exception catcher. Note that the designation of a method as being a catcher or propagator is based on a sin- gle exception. Suppose a method throws two exceptions. This method can be a catcher of the first exception and a propagator of the second exception. Let’s consider the sequence of method calls shown in Figure 8.4. Method A calls method B, method B in turn calls method C, and so forth. Notice the stack trace in the figure. Every time a method is executed, the method’s name is placed on top of the stack. By the time method D is executed, we have A, B, C, and D in the stack. When an exception is thrown, the system searches down the stack from the top, looking for the first matching exception catcher. Method D throws an exception, but no matching catch block exists in the method, so method D is an exception propagator. The system then checks method C. This method is also an exception propagator. Finally, the system locates the matching catch block in method B, and therefore, method B is the catcher for the exception thrown by method D. 450 Chapter 8 Exceptions and Assertions exception thrower exception catcher exception propagator wu23399_ch08.qxd 12/15/06 19:57 Page 450
  • 478. Method A also includes the matching catch block, but it will not be executed because the thrown exception is already caught by method B, and method B does not propagate this exception. Although the technique is not used often, an exception catcher can also be set to propagate the caught exception. For example, if we rewrite method B as try { C(); } catch (Exception e) { ... //do something here throw e; //propagate the caught exception to the //method below this one in the trace stack } it is both a catcher and a propagator. With the modified method B, method A’s matching catch block will get executed, because method B, in addition to handling the exception, throws the same exception, causing the system to look for a match- ing catcher down the stack. We have one last detail to complete the description of the exception propaga- tion mechanism. If a method is an exception propagator, we need to modify its 8.3 Propagating Exceptions 451 Call sequence Stack trace Method A Catcher Propagator Propagator try { B(); } catch (Exception e) { output.println(A); } Method B try { C(); } catch (Exception e) { output.println(B); } Method C D(); Method D Method A Method B D C B A C B A B A Method C Method D if (cond) { throw new Exception(); } A Figure 8.4 A sequence of method calls among the exception throwers.Method D throws an instance of Exception. The green arrows indicate the direction of calls.The red arrows show the reversing of call sequence, looking for a matching catcher.Method B is the catcher in this example.The call sequence is traced by using a stack.(Note: output == System.out.) wu23399_ch08.qxd 12/15/06 19:57 Page 451
  • 479. header to declare the type of exceptions the method propagates. We use the reserved word throws for this declaration. Methods C and D in Figure 8.4 must have the fol- lowing declaration (visibility modifier and return type are not relevant here): void C( ) throws Exception { ... } void D( ) throws Exception { ... } Without the required throws Exception clause, the program will not compile. There is one exception (no pun intended) to this rule. For the exceptions of the type called runtime exceptions, the throws clause is optional. For example, the getAge method of AgeInputVer1 does not include the throws clause be- cause InputMismatchException is a runtime exception. Its being optional means we can include it to explicitly state the fact if we want to. If we restate the declaration to public int getAge(String prompt) throws InputMismatchException { ... } the code will compile just fine. We will explain further about different types of exceptions in Section 8.4. Now that the exception propagation mechanism is explained, let’s study how we can apply it in designing useful service classes. First, consider the Fraction class from Chapter 7. The setDenominator method of the Fraction class was defined as follows: public void setDenominator(int denom) { if (denom == 0) { System.out.println(Fatal Error); System.exit(1); } denominator = denom; } We stated in Chapter 7 that it is too drastic to terminate a whole program when one attempts (inadvertently or otherwise) to set the denomintor to 0. Throwing an ex- ception is a much better approach. Here’s the modified method that throws an IllegalArgumentException when the value of 0 is passed as an argument: public void setDenominator(int denom) throws IllegalArgumentException { 452 Chapter 8 Exceptions and Assertions wu23399_ch08.qxd 12/15/06 19:57 Page 452
  • 480. if (denom == 0) { throw new IllegalArgumentException( Denominator cannot be 0); } denominator = denom; } Now let’s study another example. Consider the AgeInputVer3 class. It dis- allows input of negative integers. When that happens, an exception is thrown. Instead of disallowing only negative integers, wouldn’t it make more sense to restrict the valid input by specifying the lower and upper bounds? For example, we may want to restrict the input to an integer between 10 and 20 for one application and between 0 and 250 (e.g., entering the age of a building on the campus) for another application. To illustrate this concept, we will define the fourth class, AgeInputVer4, that allows the client programmers to specify the lower and upper bounds of acceptable input values. The client specifies the lower and upper bounds at the time of object creation, for example, AgeInputVer4 input = new AgeInputVer4(10, 20); This constructor will set the lower and upper bounds to 0 and 99, respectively. The lower and upper bounds are kept as data members lowerBound and upperBound, respectively, and they are initialized in the constructor. How should the getAge respond when it detects the input is outside the range of the client-designated lower and upper bounds? Instead of catching it, we will propagate the thrown exception to the caller of this method. Our responsi- bility as a provider of the service class is to tell the client by throwing an excep- tion when a condition set by the client is violated. We will let the client handle the thrown exception. The condition is set by the client, so it is more appropriate for this client to decide what to do in case of an exception. For the number format exception, the getAge method is still the catcher because this exception is thrown when a condition not dependent on any one specific client is violated. This exception is not a client-specific exception, but a generic exception suitably han- dled by the service class. So the modified getAge method is a propagator of an Exception (thrown when the bounds set by the client are violated) and a catcher of an InputMismatchException (thrown when the input is not an integer). Here’s the method: public int getAge(String prompt) throws Exception { int age; while (true) { System.out.print(prompt); try { age = scanner.nextInt(); 8.3 Propagating Exceptions 453 Propagates an Exception wu23399_ch08.qxd 12/15/06 19:57 Page 453
  • 481. if (age lowerBound || age upperBound) { throw new Exception(Input out of bound); } return age; } catch (InputMismatchException e) { scanner.next(); System.out.println(Input is invalid.n + Please enter digits only); } } } 454 Chapter 8 Exceptions and Assertions No catch block for Exception Don’t catch an exception that is thrown as a result of violating the condition set by the client programmer.Instead,propagate the exception back to the client programmer’s code and let him or her handle it. The second getAge method that uses a default prompt calls this method, so we need to rewrite the second getAge method as public int getAge() throws Exception { return getAge(DEFAULT_MESSAGE); } We have to specify the additional data members and the constructors to com- plete the AgeInputVer4 class. The new data members are declared as private static final int DEFAULT_LOWER_BOUND = 0; private static final int DEFAULT_UPPER_BOUND = 99; private int lowerBound; private int upperBound; What about the constructors? Are the following constructors acceptable? public AgeInputVer4( ) { this(DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND); } public AgeInputVer4(int low, int high) { lowerBound = low; upperBound = high; scanner = new Scanner(System.in); } This call can throw an Exception so the method header must include the correct throws clause. wu23399_ch08.qxd 12/15/06 19:58 Page 454
  • 482. Bad Version Yes, if we didn’t know about exception handling. But now with the knowledge of exception handling, we can make the class more robust by ensuring that low is less than or equal to high. If this condition is not met, then we throw an exception. The IllegalArgumentException class is precisely the class we can use for this situation. Here’s the more robust constructor: public AgeInputVer4(int low, int high) throws IllegalArgumentException { if (low high) { throw new IllegalArgumentException( Low ( + low + ) was + larger than high( + high + )); } else { lowerBound = low; upperBound = high; scanner = new Scanner(System.in); } } Now, what about the default constructor? Since the default constructor calls the other two-argument constructor, which can throw an exception, this constructor must handle the exception. One approach is to propagate the exception by declaring it as public AgeInputVer4( ) throws IllegalArgumentException { this(DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND); } This declaration is problematic, however, because when we use the throws clause, we are announcing that this method can potentially throw an exception. But this constructor will never throw an exception as long as the class is programmed cor- rectly. The only time this constructor can throw an exception is when we set the value for DEFAULT_LOWER_BOUND or DEFAULT_UPPER_BOUND incorrectly. It is an internal error and must be corrected. Since this constructor should not throw an ex- ception, we might be tempted to make this constructor an exception catcher as public AgeInputVer4( ) { try { this(DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND); } catch (IllegalArgumentException e) { //never happens, so do nothing } } Logically, this is what we want to accomplish. But syntactically, it is an error. Java requires the call to another constructor using the reserved word this to be the first 8.3 Propagating Exceptions 455 wu23399_ch08.qxd 12/15/06 19:58 Page 455
  • 483. statement. In the bad version, the try statement is the first statement. To correct this problem, we can define a private method init as private void init(int low, int high) { lowerBound = low; upperBound = high; scanner = new scanner(System.in); } and write the two constructors as public AgeInputVer4( ) { init(DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND); } public AgeInputVer4(int low, int high) throws IllegalArgumentException { if (low high) { throw new IllegalArgumentException( Low ( + low + ) was + larger than high( + high + )); } else { init(low, high); } } Here’s the complete AgeInputVer4 class: 456 Chapter 8 Exceptions and Assertions /* Chapter 8 Sample Class: Class to input age File: AgeInputVer4.java */ import javax.swing.*; class AgeInputVer4 { private static final String DEFAULT_MESSAGE = Your age:; private static final int DEFAULT_LOWER_BOUND = 0; private static final int DEFAULT_UPPER_BOUND = 99; private int lowerBound; private int upperBound; private Scanner scanner; Data members wu23399_ch08.qxd 12/15/06 19:58 Page 456
  • 484. public AgeInputVer4( ) { init(DEFAULT_LOWER_BOUND, DEFAULT_UPPER_BOUND); } public AgeInputVer4(int low, int high) throws IllegalArgumentException { if (low high) { throw new IllegalArgumentException( Low ( + low + ) was + larger than high( + high + )); } else { init(low, high); } } public int getAge() throws Exception { return getAge(DEFAULT_MESSAGE); } public int getAge(String prompt) throws Exception { int age; while (true) { System.out.print(prompt); try { age = scanner.nextInt(); if (age lowerBound || age upperBound) { throw new Exception(Input out of bound); } return age; //input okay so return the value exit } catch (InputMismatchException e) { scanner.next(); System.out.println(Input is invalid.n + Please enter digits only); } } } private void init(int low, int high) { lowerBound = low; upperBound = high; scanner = new Scanner(System.in); } } 8.3 Propagating Exceptions 457 Constructors getAge init wu23399_ch08.qxd 12/15/06 19:58 Page 457
  • 485. 458 Chapter 8 Exceptions and Assertions 1. What’s wrong with the following code? public void check(int num) { if (num 0) { throw new Exception(); } } 2. What is the difference between the reserved words throw and throws? 3. What’s wrong with the following code? public InputMismatchException getData( ) { Scanner scanner = new Scanner(System.in); try { System.out.print(Input: ); int num = scanner.nextInt(); return num; } } 8.4 Types of Exceptions We mentioned briefly in Section 8.1 that all types of thrown errors are instances of the Throwable class or its subclasses. Serious errors that signal abnormal conditions are represented by the instances of the Error class or its subclasses. Exceptional cases that common applications are expected to handle are represented by the instances of the Exception class or its subclasses. Figure 8.5 shows a very small por- tion of the inheritance hierarchy rooted in the Throwable class. There are two types of exceptions: checked and unchecked. A checked exception is an exception that is checked at compile time. All other exceptions are unchecked exceptions, also called runtime exceptions, because they are unchecked at compile time and are detected only at runtime. Trying to divide a number by 0 (ArithmeticException) and trying to convert a string with letters to an integer (NumberFormatException) are two examples of runtime exceptions. If a method is a propagator (a method that throws but does not catch an exception) of checked exceptions, then the method must have the throws clause. If a method is a propagator of runtime exceptions or errors (instances of Error or its sub- classes), the throws clause is optional. When we call a method that can throw checked exceptions, then we must use the try-catch statement and place the call in the try block, or we must modify our method header to include the appropriate throws clause. When we call a method that can throw runtime exceptions or errors, then there’s is no such requirement. We just make a call in our method. Figure 8.6 shows the valid callers of a method that throws checked exceptions, and Figure 8.7 shows the valid callers of a method that throws runtime, or unchecked, exceptions. checked and unchecked exception wu23399_ch08.qxd 12/15/06 19:58 Page 458
  • 486. Enforcing the requirement of explicitly handling runtime exceptions means, for all methods we write, that they must either have the throws clause in the header or have the try-catch statement in the body because almost every method we call from our methods can throw runtime exceptions. This is hardly effective program- ming so we don’t have to handle runtime exceptions explicitly in the program. For the errors of type Error and its subclasses, they indicate problems too serious for any ordinary application to handle, so we are not required to handle them explicitly. There’s really nothing we can do even if we catch them, so we don’t. 8.4 Types of Exceptions 459 IllegalArgumentException NumberFormatException ArithmeticException NullPointerException RuntimeException AssertionError Error IOException Exception Throwable Figure 8.5 Some classes in the inheritance hierarchy from the Throwable class.There are over 60 classes in the hierarchy. Caller A (catcher) void callerA( ) { try { doWork( ); } catch (Exception e) { ... } Caller B (propagator) void callerB( ) throws Exception { ... doWork( ); ... } doWork throws Exception public void doWork throws Exception { ... throw new Exception(); ... } Figure 8.6 Callers of a method that can throw a checked exception must explicitly include the try-catch statement in the method body or the throws clause in the method header. wu23399_ch08.qxd 12/15/06 19:58 Page 459
  • 487. 460 Chapter 8 Exceptions and Assertions Caller A (catcher) void callerA( ) { try { doWork( ); } catch ( RuntimeException e) { ... } Caller B (propagator) void callerB( ) throws RuntimeException { ... doWork( ); ... } Caller C (propagator) void callerC( ) { ... doWork( ); ... } doWork throws RuntimeException public void doWork { ... throw new RuntimeException(); ... } This is the most common style for runtime exceptions. Notice that caller C is a propagator implicitly. Figure 8.7 It is optional for the callers of a method that can throw runtime,or unchecked,exceptions to include the try-catch statement in the method body or the throws clause in the method header. If a method throws a checked exception,the caller of this method must explicitly in- clude the try-catch statement or the throws clause in the method header.If a method throws a runtime,or unchecked,exception,the use of the try-catch statement or the throws clause is optional. 1. Is this code wrong? public void check(int num) { if (num 0) { throw new IllegalArgumentException(); } } 2. What is the difference between the checked and unchecked exceptions? wu23399_ch08.qxd 12/15/06 19:58 Page 460
  • 488. 8.5 Programmer-Defined Exceptions In the AgeInputVer4 class, the getAge methods throw an instance of the Exception class. The catch clause of the caller of the getAge method can use the getMessage method to retrieve the error message or use printStackTrace to display the sequence of method calls from the main method to the method that threw an exception. But there’s no way for the client to get any other useful information such as the value actually entered by the user. Instead of using generic exception classes, we can de- fine our own exception classes so we can attach useful information to the exception objects. Let’s define a class named AgeInputException as a subclass of the Exception class. To provide useful information to the client, we will define the class so the instances will carry three pieces of information: lower bound, upper bound, and the value entered by the user (in addition to the message inherited from the Exception class). We will define three public methods to access these data. Here’s the class definition: 8.5 Programmer-Defined Exceptions 461 /* Chapter 8 Sample Class: Customized Exception Class File: AgeInputException.java */ class AgeInputException extends Exception { private static final String DEFAULT_MESSAGE = Input out of bounds; private int lowerBound; private int upperBound; private int value; public AgeInputException(int low, int high, int input) { this(DEFAULT_MESSAGE, low, high, input); } public AgeInputException(String msg, int low, int high, int input) { super(msg); if (low high) { throw new IllegalArgumentException(); } lowerBound = low; upperBound = high; value = input; } wu23399_ch08.qxd 12/15/06 19:58 Page 461
  • 489. public int lowerBound() { return lowerBound; } public int upperBound() { return upperBound; } public int value() { return value; } } 462 Chapter 8 Exceptions and Assertions The new AgeInputVer5 class is essentially the same as the AgeInputVer4 class except the getAge method of the new class throws an AgeInputException. A sample main class that uses the AgeInputVer5 is as follows: /* Chapter 8 Sample Program: Input a person’s age File: Ch8TestAgeInputVer5.java */ class Ch8TestAgeInputVer5 { public static void main( String[] args ) { int entrantAge; try { AgeInputVer5 input = new AgeInputVer5(25, 50); entrantAge = input.getAge(Your Age:); System.out.println(Input Okay. Age = + entrantAge); } catch (AgeInputException e) { System.out.println( Error: + e.value() + is entered. It is + outside the valid range of [ + e.lowerBound() + , + e.upperBound() + ]); } } } e’s methods are called to get info wu23399_ch08.qxd 12/15/06 19:58 Page 462
  • 490. 8.6 Assertions In this section we will describe a Java assertion and explain how to use it effec- tively in our programs. A Java assertion is a language feature we use to detect logical errors in a program. We will illustrate the key points with a very simple class that includes a logical error. Because the sample class is simple, the use of assertion may not seem so helpful. Keep in mind that this class is for illustrative purposes only. The real benefit of using the assertion feature becomes obvious when the program logic gets more complex and the number of classes in the program increases. Here’s a bank account class that allows withdrawals and deposits. There’s one logical error in the class: class BankAccount { private double balance; public BankAccount(double initialBalance) { balance = initialBalance; } 8.6 Assertions 463 To provide useful information to the client programmers when an exception occurs, define a new exception class.Make this customized exception class a subclass of Exception. When we create a new customized exception class, we should define it as a checked exception, and the most logical choice for its superclass is the Exception class. We should not define the customized exception class as an unchecked exception. If we did, then the client programmers would have an option of omit- ting the try-catch statement or the throws clause in their code. This is not a good idea.The goal of defining a customized exception class is to ensure that the client programmers handle the thrown exceptions of the customized class explicitly in their code,to increase the robustness of the whole program. 1. When do we want to define a customized exception class? 2. Should a customized exception class be a checked or unchecked exception? wu23399_ch08.qxd 12/15/06 19:58 Page 463
  • 491. public void deposit(double amount) { double oldBalance = balance; balance -= amount; assert balance oldBalance; } public void withdraw(double amount) { double oldBalance = balance; balance -= amount; assert balance oldBalance; } public double getBalance( ) { return balance; } } Notice the two occurences of the reserved word assert in the class definition. The syntax for the assert statement is assert boolean expression ; where boolean expression represents the condition that must be true if the code is working correctly. When the statement is executed, the boolean expression is evaluated. If it results in true, then the normal execution of the program continues. Otherwise, an AssertionError (subclass of Error) is thrown. In this example, we want to assert that balance is more than oldBalance when we deposit and less than oldBalance when we withdraw, so we write assert balance oldBalance; and assert balance oldBalance; at the end of the deposit and withdraw methods, respectively. Now let’s see what happens when we use this BankAccount class in our pro- gram. Here’s a simplistic main class to test the BankAccount class: import javax.swing.*; class Ch8TestAssertMain { public static void main(String[] args) { BankAccount acct = new BankAccount(200); acct.deposit(25); System.out.println( Current Balance: + acct.getBalance()); } } 464 Chapter 8 Exceptions and Assertions Here’s a logical error. We should add the amount. If this boolean expression results in false, then an AssertionError is thrown. wu23399_ch08.qxd 12/15/06 19:58 Page 464
  • 492. To run this program with the assertion feature enabled, we must include the designation -ea as follows: java -ea Ch8TestAssertMain (Note: For most Java IDE, you specify this option in the Preference dialog. Please consult your Java IDE for details.) If we do not provide the -ea option, then the program is executed without checking the assertions. When do we ever want to ignore the assertions we inten- tionally included in the program? Checking all assertions included in the program can be quite costly. By having an option of enabling or disabling the assertions, we can choose to enable the assertions while developing and testing the program and disable them once the program is fully developed and tested. 8.6 Assertions 465 To run the program with assertions enabled,use java -ea main class With the assert statements enabled, executing the Ch8TestAssertMain main class will result in the following error message: Exception in thread main java.lang.AssertionError at BankAccount.deposit(BankAccount.java:13) at Ch8TestAssertMain.main(Ch8TestAssertMain.java:34) The error message indicates an AssertionError is thrown at line 13 (the actual line number would be different if the source code included comments) of the Bank- Account class, which is the assert statement assert balance oldBalance; We can use the second form of the assert statement to provide a customized error message. The syntax for the second form is assert boolean expression : expression; where expression represents the value that is passed as an argument to the con- structor of the AssertionError class. The value serves as the detailed message of a thrown error. For example, using the second form, we can rewrite the deposit method as public void deposit(double amount) { double oldBalance = balance; balance -= amount; wu23399_ch08.qxd 12/15/06 19:58 Page 465
  • 493. assert balance oldBalance : Serious Error -- balance becomes less + after deposit; } With this modified deposit method, the error message will be Exception in thread main java.lang.AssertionError: Serious Error -- balance becomes less after deposit at BankAccount.deposit(BankAccount.java:14) at Ch8TestAssertMain.main(Ch8TestAssertMain.java:34) Encountering this error message during the development, we are made aware of the existence of a bug in the program. Without the assertion feature, we may not be able to detect the bug until very late in the development, or we may not be able to detect it at all. Again, for a small class such as BankAccount, the benefit of using assertions may not be obvious. However, in designing and building classes that solve diffi- cult and complex problems, effective use of assertions can be an indispensable aid, especially when it is combined with a full set of testing. We will be seeing more examples of assertions (and exceptions, also) in the later sample code. Types of Assertions The type of assertion we see in the withdraw and deposit methods is called a postcondition assertion. This assertion checks for a condition that must be true after a method is executed. Opposite to the postcondition assertion is a precondition assertion, a checking of condition that must be true before a method is executed. The third type of assertion is called a control flow invariant. Consider the following switch statement. It adds the appropriate fee to the tuition based on whether the student is a dorm resident or a dorm resident or a commuter. switch (residenceType) { case COMMUTER: totalFee = tuition + parkingFee; break; case DORM_RESIDENT: totalFee = tuition + roomAndBoard; break; } Now every student must be a dorm resident or a commuter, so if the variable resi- denceType has a value other than COMMUTER or DORM_RESIDENT, then there’s a bug somewhere. To detect such bug, we can rewrite the statement as switch (residenceType) { case COMMUTER: totalFee = tuition + parkingFee; break; 466 Chapter 8 Exceptions and Assertions postcondition assertion precondition assertion control flow invariant wu23399_ch08.qxd 12/15/06 19:58 Page 466
  • 494. Bad Version case DORM_RESIDENT: totalFee = tuition + roomAndBoard; break; default: assert false: Value of residenceType + is invalid. Value = + residenceType; } This statement documents the fact that the default case should never be executed when the program is running correctly. This is called a control flow invariant because the control must flow invariably to one of the two cases. Alternatively, we can place an assertion before the switch statement as assert (residenceType == COMMUTER || residenctType == DORM_RESIDENT) : Value of residenceType is invalid. Value = + residenceType; switch (residenceType) { case COMMUTER: totalFee = tuition + parkingFee; break; case DORM_RESIDENT: totalFee = tuition + roomAndBoard; break; } Differentiating Assertions and Exceptions Because both the assertion and the exception mechanisms are intended to improve the program reliability, their use is often mixed up. For example, if we are not attentive, we could end up using the assertion feature wrongly in places where exceptionhandling routines should be used. Consider the following case. In defining the deposit and the withdraw methods, we did not bother to check the value of the parameter (for the sake of a simplified class definition). The passed amount must be greater than zero for the methods to work correctly. How shall we include such testing? One possibility (a wrong approach) is to use the assertion feature as (we only show the withdraw method). public void withdraw(double amount) { assert amount 0; double oldBalance = balance; balance -= amount; assert balance oldBalance; } 8.6 Assertions 467 wu23399_ch08.qxd 12/15/06 19:58 Page 467
  • 495. This is not a correct use of assertions. We should not use the assertion feature to ensure the validity of an argument. In principle, we use assertions to detect the internal programming errors, and we use exceptions to notify the client program- mers of the misuse of our classes. The BankAccount class is intended as a service class used by many different programs. It is the responsibility of the client pro- grammers to pass the valid arguments. If they don’t, then we throw an exception to notify them of the misuse. Another problem is that assertions can be enabled or dis- abled when the program is run. But the validity checking of the arguments should never be disabled. 468 Chapter 8 Exceptions and Assertions Use assertions to detect internal errors.Use exceptions to notify the client program- mers of the misuse of our class. The correct way to implement the methods is as follows (only the withdraw method is shown here): public void withdraw(double amount) throws IllegalArgumentException { if (amount = 0) { throw new IllegalArgumentException( Amount must be positive); } double oldBalance = balance; balance -= amount; assert balance oldBalance; } 1. Why is the following code wrong? public void doWork(int num) { assert num 0; total += num; } 2. Name three types of assertions. wu23399_ch08.qxd 12/15/06 19:58 Page 468
  • 496. Sample Development Keyless Entry System We will develop a program that simulates a secure keyless entry system for a dormi- tory. Inside the entrance hall of a dorm, there is an entry system where the dorm resi- dents must enter their names, room numbers, and passwords. Upon entry of valid data, the system will unlock the inner door that leads to the dorm’s living quarters.To imple- ment this program, two helper classes are provided. The Door class simulates unlock- ing of the inner door.The Dorm class manages resident information. An instance of the Dorm class is capable of adding and deleting resident information, reading and saving resident information from and to a file, and retrieving information if given the resi- dent’s name. We can verify the validity of the entered data by checking them against the information kept by a Dorm object. 8.7 Sample Development 469 8.7 Sample Development We can turn our simulation program into a real one by replacing the Door class with a class that actually controls the door. Java provides a mechanism called Java Native Interface (JNI) which can be used to embed a link to a low- level device driver code, so calling the open method actually unlocks the door. Problem Statement Implement a sentry program that asks for three pieces of information: resident’s name,room number,and a password.A password is any sequence of characters ranging in length from 4 to 8 and is unique to an individual dorm resident. If everything matches,then the system unlocks and opens the door.We assume no two residents have the same name. Use the provided support classes Door and Dorm. Overall Plan To provide a complete system, we actually have to write two separate programs. The first one is the administrative module for adding, removing, and updating the resident information.The second is the user module that interacts with the residents. Figure 8.8 shows the program diagrams for the two modules. In this section, we implement the user module. The administrative module is left as an exercise. To begin our development effort, we must first find out the capabilities of the Dorm and Door classes. Also, for us to implement the class correctly, we need the specification of the Resident class. wu23399_ch08.qxd 12/15/06 19:58 Page 469
  • 497. 8.7 Sample Development—continued Resident The Resident class maintains information on individual dorm residents.We will be deal- ing with many instances of this class in the program. A password assigned to a resident must be a sequence of 4 to 8 characters. For this class to work properly with the Dorm class,the class must include these public methods: 470 Chapter 8 Exceptions and Assertions Dorm Door Resident User module Dorm Resident A helper class provided to us A class we implement One or more classes we implement Administrative module Figure 8.8 Program diagrams for the user and administrative modules.Notice the same Dorm and Resident classes are used in both programs.User and administrative modules will include one or more classes (at least one is programmer-defined). Public Methods of Resident public Resident( ) Default constructor that creates a Resident object with name =“unassigned” , room =“000” , and id =“@13” . public Resident(String name, String room, String password) throws IllegalArgumentException Creates a Resident object with the passed values. IllegalArgumentException is thrown when the given password has less than four or more than eight characters. wu23399_ch08.qxd 12/15/06 19:58 Page 470
  • 498. 8.7 Sample Development 471 public void setName(String name) Assigns the name. public void setPassword(String id) throws IllegalArgumentException Assigns the password.IllegalArgumentException is thrown when the given password has less than four or more than eight characters. public void setRoom(String room) Assigns the room. public String getName( ) Returns the name. public String getPassWord( ) Returns the password. public String getRoom( ) Returns the room number. One important restriction to the Resident class is the requirement for the class to implement the Serializable interface. Because the Resident objects are saved to a file, Java requires the class definition to include the phrase implements Serializable as import java.io.*; class Resident implements Serializable { ... } Details on the significance of the clause implements Serializable will be given when we discuss the file input and output in Chapter 12. For any object we need to save to a file,its class definition must include the phrase implements Serializable. Dorm The Dorm class is a helper class provided to us. A Dorm object is capable of managing a list of Resident objects. It allows the client to add, delete, and retrieve Resident objects. In addition, it is capable of saving a list to a file or reading a list from a file. By wu23399_ch08.qxd 12/15/06 19:58 Page 471
  • 499. 8.7 Sample Development—continued having these file input and output features, our program can work with different lists of residents much as a word processor can work with different documents (files).The class definition is as follows: 472 Chapter 8 Exceptions and Assertions Public Methods of Dorm public Dorm( ) Default constructor that creates a Dorm object. public Dorm(String filename) Creates a Dorm object with the resident list read from the file with the name filename.Throws FileNotFoundException when the designated file cannot be found and IOException when the file cannot be read. public void openFile(String filename) Reads the resident list from the designated file.Throws FileNotFoundException when the designated file cannot be found and IOException when the file cannot be read. public void saveFile(String filename) Saves the resident list to the designated file. Throws IOException when the file cannot be saved. public void add(Resident resident) Adds the resident to the list. Throws IllegalArgumentException when a resident with the same name already exists in the list.We do not allow duplicate names.Every resident must have a unique name. public void delete(String name) Deletes the designated resident from the list.If no such resident is in the list, nothing happens. public Resident getResident(String name) Returns the Resident object with the given name.Returns null if no matching Resident is found. public String getResidentList( ) Returns a list of residents as a String.A line separator is used after each resident.For each resident,the list contains his or her name,room number, and password. Door The Door class is another helper class. It simulates the opening of the door. In a real control program, a Door object can have an embedded low-level device driver code, wu23399_ch08.qxd 12/15/06 19:58 Page 472
  • 500. so it really opens the door. The class definition is as follows: 8.7 Sample Development 473 Public Methods of Door public Door( ) Default constructor that creates a new Door object. public void open() Opens the door.For this simulator class,it displays a simple message dialog. Now let’s study the overall design of the program. In addition to the given helper classes and the Resident class, what other classes should we define for this program? As the number of classes gets larger, we need to plan the classes carefully. For this program, we will define a controller class named Ch8EntranceMonitor whose instance will manage all other objects.We will set this class as the program’s main class.The user interface of the program is handled by the InputHandler class. Its instance is used to allow the user to enter his or her name, room number, and password. After the required input data are entered by the user, a Ch8EntranceMonitor checks the validity of the input data with help from a service Dorm object. If the Dorm object confirms the input data, the controller then instructs another service object, an instance of Door, to open the door. The following is our working design document, and Figure 8.9 is the program diagram. overall design Ch8EntranceMonitor InputHandler Dorm Door JOptionPane Resident User module Figure 8.9 The program diagram for the Ch8EntranceMonitor program. There are three classes in the user module. wu23399_ch08.qxd 12/15/06 19:58 Page 473
  • 501. 8.7 Sample Development—continued We will implement the user module in three major steps: 1. Define the Resident class and explore the Dorm class.Start with a program skeleton to test the Resident class. 2. Define the user interface InputHandler class.Modify the top-level control class as necessary. 3. Finalize the code by making improvements and tying up loose ends. Step 1 Development: Program Skeleton Our first task is to find out about the given Dorm class. (The Door class is a very simple simulator class so there’s not much to explore.) To be able to test-run the Dorm class, we must provide the Resident class, so this will be our first step. The purpose of the skeleton main class in this step is to verify the operations of the Dorm class. The specification for the Resident class was given to us,so our task is to implement it according to the specification.No design work is necessary. When we can interact with an instance of the Dorm class correctly, it confirms that our implementation of the Resident class is working. To verify the key operations of the Dorm class, the top-level supervisor object Ch8EntranceMonitor will open a file and list the contents of the file. Here’s the Resident class: 474 Chapter 8 Exceptions and Assertions Design Document: Ch8EntranceMonitor Class Purpose Ch8EntranceMonitor The top-level control object manages other objects in the program.This is an instantiable main class. Door The given predefined class simulates the opening of a door. Dorm The given predefined class maintains a list of Resident objects. InputHandler The user interface class is for handling input routines. program classes develop- ment steps step 1 design step 1 code /* Chapter 8 Sample Development: Keyless Entry System. File: Resident.java */ wu23399_ch08.qxd 12/15/06 19:58 Page 474
  • 502. import java.io.*; class Resident implements Serializable { private String name; private String room; private String password; public Resident( ) { this(unassigned, 000, @13); } public Resident(String name, String room, String pwd) throws IllegalArgumentException { setName(name); setRoom(room); setPassword(pwd); } public String getName( ) { return name; } public String getPassword( ) { return password; } public String getRoom( ) { return room; } public void setName(String name) { this.name = name; } public void setPassword(String pwd) { int length = pwd.length(); if (length 4 || length 8) { throw new IllegalArgumentException(); } else { this.password = pwd; } } public void setRoom(String room) { this.room = room; } } 8.7 Sample Development 475 Data members Constructors Accessors Mutators wu23399_ch08.qxd 12/15/06 19:58 Page 475
  • 503. 8.7 Sample Development—continued The skeleton main class is defined as follows: 476 Chapter 8 Exceptions and Assertions /* Chapter 8 Sample Development: Keyless Entry System. (Step 1) File: Ch8EntranceMonitor.java */ import javax.swing.*; import java.io.*; class Ch8EntranceMonitor { //Step 1 main class private Dorm manager; private Scanner scanner; public Ch8EntranceMonitor( ) { manager = new Dorm(); scanner = new Scanner(System.in); } public static void main(String[] args) { Ch8EntranceMonitor sentry = new Ch8EntranceMonitor(); sentry.start(); } public void start( ) { openFile( ); String roster = manager.getResidentList(); System.out.println(roster); } private void openFile( ) { String filename; while (true) { System.out.println(File to open ('x' to cancel):); filename = scanner.next(); if (filename.equals(x)) {//input routine is canceled System.out.println(Program is canceled.); System.exit(0); } start openFile wu23399_ch08.qxd 12/15/06 19:58 Page 476
  • 504. try { manager.openFile(filename); return; } catch (FileNotFoundException e) { System.out.println(No such file); } catch (IOException e) { System.out.println(Error in reading file); } } } 8.7 Sample Development 477 The purpose of step 1 testing is to verify that the Dorm class is used correctly to open a file and get the contents of the file.To test it, we need a file that contains the resi- dent information. A sample test file can be created by executing the following program, which we can modify to create other test data files. step 1 test /* Chapter 8 Sample Development: Keyless Entry System. A simple class to create dummy test data. File: SampleCreateResidentFile.java */ import java.util.*; import java.io.*; class SampleCreateResidentFile { public static void main(String[] args)throws IOException { Resident res; Dorm manager = new Dorm( ); res = new Resident(john, 1-101, 3457); manager.add(res); res = new Resident(java, 1-102, 4588); manager.add(res); res = new Resident(jill, 3-232, 8898); manager.add(res); wu23399_ch08.qxd 12/15/06 19:58 Page 477
  • 505. 8.7 Sample Development—continued res = new Resident(jack, 3-232, 8008); manager.add(res); Scanner scanner = new Scanner(System.in); System.out.println(Save to which file:); String filename = scanner.next(); manager.saveFile(filename); System.exit(0); //terminate the program } } 478 Chapter 8 Exceptions and Assertions Step 2 Development: Create the User Interface In the second development step, we will implement the user interface class InputHandler, whose task is to get three pieces of information. The main controller Ch8EntranceMonitor will call an InputHandler to get input data. An InputHandler will then go through a sequence of getting the three pieces of data. Once the data are entered, Ch8EntranceMonitor will ask the InputHandler for these data. The logic of Ch8EntranceMonitor can be expressed as follows: InputHandler input = new InputHandler(); . . . input.getInput(); String name = input.getName(); String room = input.getRoomNumber(); String pwd = input.getPassword(); Given the input data,we can check for the match as Dorm manager = new Dorm(); . . . Resident res = manager.getResident(name); if (res == null) { System.out.println(Invalid Entry); step 2 design wu23399_ch08.qxd 12/15/06 19:58 Page 478
  • 506. } else if (res.getName().equals(name) res.getRoom().equals(room) res.getPassword().equals(password)) { door.open(); } else { System.out.println (Invalid Entry); } The getInput method of the InputHandler class calls the scanner three times to get the name, room, and password. Each input is recorded in the corresponding data member.The accessors, such as getName, will simply return the value of the requested data member. We will list first the InputHandler class and then the modified Ch8Entrance- Monitor class.Here’s the InputHandler class: 8.7 Sample Development 479 step 2 code /* Chapter 8 Sample Development: Keyless Entry System File: InputHandler.java */ import java.util.*; class InputHandler { private static final String BLANK = ; private String name; private String room; private String pwd; private Scanner scanner; public InputHandler( ) { name = BLANK; room = BLANK; pwd = BLANK; scanner = new Scanner(System.in); } public void getInput( ) { System.out.print(Enter Name:); name = scanner.next(); System.out.print(Enter Room No.:); room = scanner.next(); System.out.print(Enter Password:); pwd = scanner.next(); } Data members Constructor getInput wu23399_ch08.qxd 12/15/06 19:58 Page 479
  • 507. 8.7 Sample Development—continued public String getName( ) { return name; } public String getRoom( ) { return room; } public String getPassword( ) { return pwd; } } 480 Chapter 8 Exceptions and Assertions The main class is now modified to control an InputHandler object and to check en- tered information as the resident list maintained by a Dorm object. Here’s the step 2 Ch8EntranceMonitor class: /* Chapter 8 Sample Development: Keyless Entry System. File: Ch8EntranceMonitor.java (Step 2) */ import java.util.*; import java.io.*; class Ch8EntranceMonitor { private Dorm manager; private Door door; private InputHandler input; private Scanner scanner; public Ch8EntranceMonitor( ) { manager = new Dorm(); scanner = new Scanner(System.in); input = new InputHandler(); door = new Door(); } Data members Constructors Accessors wu23399_ch08.qxd 12/15/06 19:58 Page 480
  • 508. public static void main(String[] args) { Ch8EntranceMonitor sentry = new Ch8EntranceMonitor(); sentry.start(); } public void start( ) { openFile( ); String roster = manager.getResidentList(); //TEMP System.out.println(roster); //TEMP processInputData(); } private void openFile( ) { String filename; while (true) { System.out.println(File to open ('x' to cancel):); filename = scanner.next(); if (filename.equals(x)) {//input routine is canceled System.out.println(Program is canceled.); System.exit(0); } try { manager.openFile(filename); return; } catch (FileNotFoundException e) { System.out.println(No such file); } catch (IOException e) { System.out.println(Error in reading file); } } } private void processInputData( ) { String name, room, pwd; while (true) { input.getInput(); name = input.getName(); room = input.getRoom(); pwd = input.getPassword(); 8.7 Sample Development 481 start openFile processInputData wu23399_ch08.qxd 12/15/06 19:58 Page 481
  • 509. 8.7 Sample Development—continued validate(name, room, pwd); } } private void validate(String name, String room, String password) { Resident res = manager.getResident(name); if (res == null) { System.out.println(Invalid Entry); } else if (res.getName().equals(name) res.getRoom().equals(room) res.getPassword().equals(password)) { door.open(); } else { System.out.println(Invalid Entry); } } } 482 Chapter 8 Exceptions and Assertions Notice that the loop inside the processInputData method is an infinite loop. In other words, when the program starts, it will execute indefinitely. To terminate such a program, you must either close the Command window or select an appropriate menu choice (or click on a toolbar icon) in your Java IDE. We will discuss another way to ter- minate the program in step 3. The purpose of step 2 testing is to verify the correct behavior of an InputHandler object. We need to test both successful and unsuccessful cases. We must verify that the door is in fact opened when valid information is entered. We must also verify that the error message is displayed when there’s an error in input. We should test invalid cases such as entering nonexistent name, corrent name but wrong password, not entering all information,and so forth. Step 3 Development: Improve and Finalize There are several key improvements we can make to the program. The first and foremost is the improved user interface. Instead of getting three pieces of data individually by using a scanner, it would be nicer to have a frame window such as the one shown in Figure 8.10,where the user can enter all three pieces of information.We will describe how to develop such a frame window in Chapter 14. step 2 test validate wu23399_ch08.qxd 12/15/06 19:58 Page 482
  • 510. Another improvement is to allow the administrator to terminate the program by entering special code.This is left as an exercise. Summary 483 • Two techniques to improve program reliability are exception handling and assertion. • Exception handling is another type of control flow. • An exception represents an error condition, and when it occurs, we say an exception is thrown. • A thrown exception must be handled by either catching it or propagating it to other methods. • If the program does include code to handle the thrown exceptions, then the system will handle them. • A single method can be both a catcher and a propagator of an exception. • The standard classes described or used in this chapter are Throwable RuntimeException Error IllegalArgumentException Exception InputMismatchException IOException • The assertion feature is new to Java 2 SDK 1.4. You must use this version of the compiler to use assertions in the program. • The assertion feature is used to detect internal logic errors. Figure 8.10 A frame window that allows the user to enter the three pieces of information together. Notice the input entered for the password is displayed back to the user as a sequence of asterisks. S u m m a r y wu23399_ch08.qxd 12/15/06 19:58 Page 483
  • 511. 484 Chapter 8 Exceptions and Assertions E x e r c i s e s 1. Determine the output of the following code when the input is (a) 1, (b) 0, and (c) 12XY. Scanner scanner = new Scanner(System.in); try { int num = scanner.nextInt(); if (num != 0) { throw new Exception(Not zero); } System.out.println(I'm happy with the input.); } catch (InputMismatchException e) { System.out.println(Invalid Entry); } catch (Exception e) { System.out.println(Error: + e.getMessage()); } 2. Determine the output of the following code when the input is (a) 1, (b) 0, and (c) 12XY. This is the same question as Exercise 1, but the code here has the finally clause. Scanner scanner = new Scanner(System.in); try { int num = scanner.nextInt(); if (num != 0) { throw new Exception(Not zero); } System.out.println(I'm happy with the input.); } catch (InputMismatchException e) { System.out.println(Invalid Entry); K e y C o n c e p t s exceptions try-catch finally throws throw exception hierarchy programmer-defined exceptions assertions precondition assertions postcondition assertions wu23399_ch08.qxd 12/15/06 19:58 Page 484
  • 512. } catch (Exception e) { System.out.println(Error: + e.getMessage()); } finally { System.out.println(Finally Clause Executed); } 3. Why is the following code not a good use of the assertion? public void compute(int size) { assert size 0; //computation code comes here } 4. Modify the following code by adding the assert statement. The value of gender is either MALE or FEMALE if the program is running correctly. switch (gender) { case MALE: totalFee = tuition + parkingFee; break; case FEMALE: totalFee = tuition + roomAndBoard; break; } 5. Modify the following method by adding the assert statement. Assume the variable factor is a data member of the class. public double compute(double value) { return (value * value) / factor; } 6. Modify the getInput method of the InputHandler class from Section 8.7 so that the method will throw an exception when a blank string (a sequence of one or more blank spaces) is entered for the name, room, or password. Define a new exception class EmptyInputException. 7. The user module of the keyless entry system in Section 8.7 does not include any logic to terminate the program. Modify the program so it will terminate when the values Admin, X123, and $maTrix%TwO$ are entered for name, room, and password, respectively. Development Exercises For the following exercises, use the incremental development methodology to implement the program. For each exercise, identify the program tasks, create a design document with class descriptions, and draw the program diagram. Map out the development steps at the start. Present any design alternatives and justify your selection. Be sure to perform adequate testing at the end of each development step. Exercises 485 wu23399_ch08.qxd 12/15/06 19:58 Page 485
  • 513. 8. In the sample development, we developed the user module of the keyless entry system. For this exercise, implement the administrative module that allows the system administrator to add and delete Resident objects and modify information on existing Resident objects. The module will also allow the user to open a list from a file and save the list to a file. Is it proper to implement the administrative module by using one class? Wouldn’t it be a better design if we used multiple classes with each class doing a single, well-defined task? 9. Write an application that maintains the membership lists of five social clubs in a dormitory. The five social clubs are the Computer Science Club, Biology Club, Billiard Club, No Sleep Club, and Wine Tasting Club. Use the Dorm class to manage the membership lists. Members of the social clubs are Resident objects of the dorm. Use a separate file to store the membership list for each club. Allow the user to add, delete, and modify members of each club. 486 Chapter 8 Exceptions and Assertions wu23399_ch08.qxd 12/15/06 19:58 Page 486
  • 514. Characters and Strings O b j e c t i v e s After you have read and studied this chapter,you should be able to • Declare and manipulate data of the char type. • Write string processing programs,using String,StringBuilder, and StringBuffer objects. • Specify regular expressions for searching a pattern in a string. • Differentiate the String,StringBuilder, and StringBuffer classes and use the correct class in solving a given task. • Tell the difference between equality and equivalence testings for String objects. • Use the Pattern and Matcher classes. 487 9 wu23399_ch09.qxd 12/15/06 20:05 Page 487
  • 515. arly computers in the 1940s and 1950s were more like gigantic calculators because they were used primarily for numerical computation. However, as computers have evolved to possess more computational power, our use of computers is no longer limited to numerical computation. Today we use computers for processing infor- mation of diverse types. In fact, most application software today such as Web browsers, word processors, database management systems, presentation software, and graphics design software is not intended specifically for number crunching. These programs still perform numerical computation, but their primary data are text, graphics, video, and other nonnumerical data. We have already seen examples of nonnumerical data processing. We introduced the String class and string process- ing in Chapter 2. A nonnumerical data type called boolean was used in Chapters 5 and 6. In this chapter, we will delve more deeply into the String class and present advanced string processing. We will also introduce the char data type for represent- ing a single character and the StringBuffer class for an efficient operation on a certain type of string processing. 9.1 Characters In Java single characters are represented by using the data type char. Character constants are written as symbols enclosed in single quotes, for example, ‘a’, ‘X’, and ‘5’. Just as we use different formats to represent integers and real numbers using 0s and 1s in computer memory, we use special codes of 0s and 1s to represent single characters. For example, we may assign 1 to represent ’A’ and 2 to repre- sent ‘B’. We can assign codes similarly to lowercase letters, punctuation marks, digits, and other special symbols. In the early days of computing, different com- puters used not only different coding schemes but also different character sets. For example, one computer could represent the symbol 1 ⁄4, while other computers could not. Individualized coding schemes did not allow computers to share infor- mation. Documents created by using one scheme are complete gibberish if we try to read these documents by using another scheme. To avoid this problem, U.S. computer manufacturers devised several coding schemes. One of the coding schemes widely used today is ASCII (American Standard Code for Information Interchange). We pronounce ASCII “ăs kē.” Table 9.1 shows the 128 standard ASCII codes. Adding the row and column indexes gives you the ASCII code for a given character. For example, the value 87 is the ASCII code for the character ‘W’. Not all characters in the table are printable. ASCII codes 0 through 31 and 127 are nonprintable control characters. For example, ASCII code 7 is the bell (the computer beeps when you send this character to output), and code 9 is the tab. 488 Chapter 9 Characters and Strings I n t r o d u c t i o n E char ASCII wu23399_ch09.qxd 12/15/06 20:05 Page 488
  • 516. To represent all 128 ASCII codes, we need 7 bits ranging from 000 0000 (0) to 111 1111 (127). Although 7 bits is enough, ASCII codes occupy 1 byte (8 bits) because the byte is the smallest unit of memory you can access. Computer manu- facturers use the extra bit for other nonstandard symbols (e.g., lines and boxes). Using 8 bits, we can represent 256 symbols in total—128 standard ASCII codes and 128 nonstandard symbols. 9.1 Characters 489 When we use a word processor to create a document, the file that contains the document includes not only the contents but also the formatting information. Since each software company uses its own coding scheme for storing this informa- tion, we have to use the same word processor to open the document. Often it is even worse.We cannot open a document created by a newer version of the same word processor with an older version. If we just want to exchange the text of a document, then we can convert it to ASCII format. Any word processor can open and save ASCII files. If we would like to retain the formatting information also, we can convert the document, using software such as Adobe Acrobat. This soft- ware converts a document (including text, formatting, images, etc.) created by different word processors to a format called PDF. Anybody with a free Acrobat Reader can open a PDF file. Many of the documents available from our website are in this PDF format. Table Table 9.1 ASCII codes 0 1 2 3 4 5 6 7 8 9 0 nul soh stx etx eot enq ack bel bs ht 10 lf vt ff cr so si dle dc1 dc2 dc3 20 cd4 nak syn etb can em sub esc fs gs 30 rs us sp ! # $ % ' 40 ( ) * + , - . / 0 1 50 2 3 4 5 6 7 8 9 : ; 60 = ? @ A B C D E 70 F G H I J K L M N O 80 P Q R S T U V W X Y 90 Z [ ] ^ _ ` a b c 100 d e f g h i j k l m 110 n o p q r s t u v w 120 x y z { | } ~ del wu23399_ch09.qxd 12/15/06 20:05 Page 489
  • 517. The standard ASCII codes work just fine as long as we are dealing with the English language because all letters and punctuation marks used in English are included in the ASCII codes. We cannot say the same for other languages. For lan- guages such as French and German, the additional 128 codes may be used to repre- sent character symbols not available in standard ASCII. But what about different currency symbols? What about non-European languages? Chinese, Japanese, and Korean all use different coding schemes to represent their character sets. Eight bits is not enough to represent thousands of ideographs. If we try to read Japanese char- acters by using ASCII, we will see only meaningless symbols. To accommodate the character symbols of non-English languages, the Unicode Consortium established the Unicode Worldwide Character Standard, commonly known simply as Unicode, to support the interchange, processing, and display of the written texts of diverse languages. The standard currently contains 34,168 distinct characters, which cover the major languages of the Americas, Europe, the Middle East, Africa, India, Asia, and Pacifica. To accommodate such a large number of distinct character symbols, Unicode characters occupy 2 bytes. Unicode codes for the character set shown in Table 9.1 are the same as ASCII codes. Java, being a language for the Internet, uses the Unicode standard for repre- senting char constants. Although Java uses the Unicode standard internally to store characters, to use foreign characters for input and output in our programs, the oper- ating system and the development tool we use for Java programs must be capable of handling the foreign characters. Characters are declared and used in a manner similar to data of other types. The declaration char ch1, ch2 = 'X'; declares two char variables ch1 and ch2 with ch2 initialized to ‘X’. We can display the ASCII code of a character by converting it to an integer. For example, we can execute System.out.println(ASCII code of character X is + (int)'X' ); Conversely, we can see a character by converting its ASCII code to the char data type, for example, System.out.println( Character with ASCII code 88 is + (char)88 ); Because the characters have numerical ASCII values, we can compare charac- ters just as we compare integers and real numbers. For example, the comparison 'A' 'c' returns true because the ASCII value of ‘A’ is 65 while that of ‘c’ is 99. 490 Chapter 9 Characters and Strings Unicode wu23399_ch09.qxd 12/15/06 20:05 Page 490
  • 518. 9.2 Strings 491 1. Determine the output of the following statements. a. System.out.println( (char) 65 ); b. System.out.println( (int) 'C' ); c. System.out.println( 'Y' ); d. if ( 'A' '?' ) System.out.println( 'A' ); else System.out.println( '?' ); 2. How many distinct characters can you represent by using 8 bits? 9.2 Strings A string is a sequence of characters that is treated as a single value. Instances of the String class are used to represent strings in Java. Rudimentary string processing was already presented in Chapter 2, using methods such as substring, length, and indexOf. In this section we will learn more advanced string processing, using other methods of the String class. To introduce additional methods of the String class, we will go through a num- ber of common string processing routines. The first is to process a string looking for a certain character or characters. Let’s say we want to input a person’s name and de- termine the number of vowels that the name contains. The basic idea is very simple: for each character ch in the string { if (ch is a vowel) { increment the counter } } There are two details we need to know before being able to translate that into actual code. First, we need to know how to refer to an individual character in the string. Second, we need to know how to determine the size of the string, that is, the num- ber of characters the string contains, so we can write the boolean expression to stop the loop correctly. We know from Chapter 2 that the second task is done by using the length method. For the first task, we use charAt. We access individual characters of a string by calling the charAt method of the String object. For example, to display the individual characters of the string Sumatra one at a time, we can write String name = Sumatra; int size = name.length( ); for (int i = 0; i size; i++) { System.out.println(name.charAt(i)); } Each character in a string has an index that we use to access the character. We use zero-based indexing; that is, the first character has index 0, the second character String charAt wu23399_ch09.qxd 12/15/06 20:05 Page 491
  • 519. has index 1, the third character has index 2, and so forth. To refer to the first char- acter of name, for example, we say name.charAt(0) Since the characters are indexed from 0 to size-1, we could express the pre- ceding for loop as for (int i = 0; i = size - 1; i++) However, we will use the first style almost exclusively to be consistent. Figure 9.1 illustrates how the charAt method works. Notice that name refers to a String object, and we are calling its charAt method that returns a value of prim- itive data type char. Strictly speaking, we must say “name is a variable of type String whose value is a reference to an instance of String.” However, when the value of a variable X is a reference to an instance of class Y, we usually say “X is an instance of Y” or “X is a Y object.” 492 Chapter 9 Characters and Strings S u m r a a t 0 1 2 5 6 3 4 String name = Sumatra; name The variable refers to the whole string. name.charAt(3) The method returns the character at position 3. Figure 9.1 An indexed expression is used to refer to individual characters in a string. If the value of a variable X is a reference to an object of class Y,then we say“ X is a Y object”or“ X is an instance of Y.” Since String is a class, we can create an instance of a class by using the new method. The statements we have been using so far, such as String name1 = Kona; String name2; name2 = Espresso; work as a shorthand for String name1 = new String(Kona); String name2; name2 = new String(Espresso); wu23399_ch09.qxd 12/15/06 20:05 Page 492
  • 520. Be aware that this shorthand works for the String class only. Moreover, although the difference will not be critical in almost all situations, they are not exactly the same. We will discuss the subtle difference between the two in Section 9.5. Here is the code for counting the number of vowels: 9.2 Strings 493 /* Chapter 9 Sample Program: Count the number of vowels in a given string File: Ch9CountVowels.java */ import java.util.*; class Ch9CountVowels { public static void main (String[] args) { Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); String name; int numberOfCharacters, vowelCount = 0; char letter; System.out.print(What is your name?); name = scanner.next( ); numberOfCharacters = name.length( ); for (int i = 0; i numberOfCharacters; i++) { letter = name.charAt(i); if (letter == 'a' || letter == 'A' || letter == 'e' || letter == 'E' || letter == 'i' || letter == 'I' || letter == 'o' || letter == 'O' || letter == 'u' || letter == 'U' ) { vowelCount++; } } System.out.println(name + , your name has + vowelCount + vowels); } } wu23399_ch09.qxd 12/15/06 20:05 Page 493
  • 521. 494 Chapter 9 Characters and Strings We can shorten the boolean expression in the if statement by using the toUpperCase method of the String class. This method converts every character in a string to uppercase. Here’s the rewritten code: /* Chapter 9 Sample Program: Count the number of vowels in a given string using toUpperCase File: Ch9CountVowels2.java */ import java.util.*; class Ch9CountVowels2 { public static void main (String[] args) { Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator?)); String name, nameUpper; int numberOfCharacters, vowelCount = 0; char letter; System.out.print(What is your name?); name = scanner.next( ); numberOfCharacters = name.length( ); nameUpper = name.toUpperCase( ); for (int i = 0; i numberOfCharacters; i++) { letter = nameUpper.charAt(i); if (letter == 'A' || letter == 'E' || letter == 'I' || letter == 'O' || letter == 'U' ) { vowelCount++; } } (name + , your name has + vowelCount + vowels); } } wu23399_ch09.qxd 12/15/06 20:05 Page 494
  • 522. Bad Version Bad Version Notice that the original string name is unchanged. A new, converted string is returned from the toUpperCase method and assigned to the second String variable nameUpper. Let’s try another example. This time we read in a string and count how many words the string contains. For this example we consider a word as a sequence of characters separated, or delimited, by blank spaces. We treat punctuation marks and other symbols as part of a word. Expressing the task in pseudocode, we have the following: read in a sentence; while (there are more characters in the sentence) { look for the beginning of the next word; now look for the end of this word; increment the word counter; } We use a while loop here instead of do–while to handle the case when the input sentence contains no characters, that is, when it is an empty string. Let’s implement the routine. Here’s our first attempt: //Attempt No. 1 static final char BLANK = ' '; Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator )); int index, wordCount, numberOfCharacters; System.out.println(Enter a sentence: ); String sentence = scanner.next( ); numberOfCharacters = sentence.length(); index = 0; wordCount = 0; while (index numberOfCharacters ) { //ignore blank spaces while (sentence.charAt(index) == BLANK) { index++; } //now locate the end of the word while (sentence.charAt(index) != BLANK) { index++; } //another word has been found, so increment the counter wordCount++; } 9.2 Strings 495 toUpperCase Skip blank spaces until a character that is not a blank space is encoun- tered.This is the begin- ning of a word. Once the beginning of a word is detected,we skip nonblank charac- ters until a blank space is encountered.This is the end of the word. wu23399_ch09.qxd 12/15/06 20:05 Page 495
  • 523. This implementation has a problem. The counter variable index is incre- mented inside the two inner while loops, and this index could become equal to numberOfCharacters, which is an error, because the position of the last character is numberOfCharacters – 1. We need to modify the two while loops so that index will not become larger than numberOfCharacters –1. Here’s the modified code: 496 Chapter 9 Characters and Strings /* Chapter 9 Sample Program: Count the number of words in a given string File: Ch9CountWords.java (Attempt 2) */ import java.util.*; class Ch9CountWords { //Attempt 2 private static final char BLANK = ' '; public static void main (String[] args) { Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator )); int index, wordCount, numberOfCharacters; System.out.println(Enter a sentence: ); String sentence = scanner.next( ); numberOfCharacters = sentence.length( ); index = 0; wordCount = 0; while ( index numberOfCharacters ) { //ignore blank spaces while (index numberOfCharacters sentence.charAt(index) == BLANK) { index++; } //now locate the end of the word while (index numberOfCharacters sentence.charAt(index) != BLANK) { index++; } //another word is found, so increment the counter wordCount++; } wu23399_ch09.qxd 12/15/06 20:05 Page 496
  • 524. //display the result System.out.println( n input sentence: + sentence ); System.out.println( Word count: + wordCount + words ); } } 9.2 Strings 497 Notice that the order of comparisons in the boolean expression index numberOfCharacters sentence.charAt(index) == BLANK is critical. If we switch the order to sentence.charAt(index) == BLANK index numberOfCharacters and if the last character in the string is a space, then an out-of-bound exception will occur because the value of index is a position that does not exist in the string sentence. By putting the expression correctly as index numberOfCharacters sentence.charAt(index) != ' ' we will not get an out-of-bound exception because the boolean operator is a shortcircuit operator. If the relation index numberOfCharacters is false, then the second half of the expression sentence.charAT(index) != BLANK will not get evaluated. There is still a problem with the attempt 2 code. If the sentence ends with one or more blank spaces, then the value for wordCount will be 1 more than the actual number of words in the sentence. It is left as an exercise to correct this bug (see Exercise 15 at the end of the chapter). Our third example counts the number of times the word Java occurs in the input. The repetition stops when the word STOP is read. Lowercase and upper- case letters are not distinguished when an input word is compared to Java, but the word STOP for terminating the loop must be in all uppercase letters. Here’s the pseudocode: javaCount = 0; while (true) { read in next word; if (word is STOP) { break; out-of-bound exception wu23399_ch09.qxd 12/15/06 20:05 Page 497
  • 525. } else if (word is Java ignoring cases) { javaCount++; } } And here’s the actual code. Pay close attention to how the strings are compared. 498 Chapter 9 Characters and Strings /* Chapter 9 Sample Program: Count the number of times the word 'java' occurs in input. Case-insensitive comparison is used here. The program terminates when the word STOP (case-sensitive) is entered. File: Ch9CountJava.java */ import java.util.*; class Ch9CountJava { public static void main (String[] args) { Scanner scanner = new Scanner(System.in); int javaCount = 0; String word; while (true) { System.out.print(Next word: ); word = scanner.next( ); if (word.equals(STOP) ) { break; } else if (word.equalsIgnoreCase(Java) ) { javaCount++; } } System.out.println('Java' count: + javaCount ); } } wu23399_ch09.qxd 12/15/06 20:05 Page 498
  • 526. String comparison is done by two methods—equals and equalsIgnoreCase— whose meanings should be clear from the example. Another comparison method is compareTo. This method compares two String objects str1 and str2 as in str1.compareTo( str2 ); and returns 0 if they are equal, a negative integer if str1 is less than str2, and a pos- itive integer if str1 is greater than str2. The comparison is based on the lexicographic order of Unicode. For example, caffeine is less than latte. Also, the string jaVa is less than the string java because the Unicode value of V is smaller than the Unicode value of v. (See the ASCII table, Table 9.1.) Some of you may be wondering why we don’t say if ( word == STOP ) We can, in fact, use the equality comparison symbol == to compare two String objects, but the result is different from the result of the method equals. We will explain the difference in Section 9.5. Let’s try another example, using the substring method we introduced in Chapter 2. To refresh our memory, here’s how the method works. If str is a String object, then the expression str.substring ( beginIndex, endIndex ) returns a new string that is a substring of str from position beginIndex to endIndex – 1. The value of beginIndex must be between 0 and str.length() – 1, and the value of endIndex must be between 0 and str.length(). In addition, the value of beginIndex must be less than or equal to the value of endIndex. Passing invalid values for beginIndex or endIndex will result in a runtime error. In this example, we print out the words from a given sentence, using one line per word. For example, given an input sentence I want to be a Java programmer the code will print out I want to be a Java programmer 9.2 Strings 499 compareTo wu23399_ch09.qxd 12/15/06 20:05 Page 499
  • 527. This sample code is similar to the previous one that counts the number of words in a given sentence. Instead of just counting the words, we need to extract the word from the sentence and print it out. Here’s how we write the code: 500 Chapter 9 Characters and Strings /* Chapter 9 Sample Program: Extract the words in a given sentence and print them, using one line per word. File: Ch9ExtractWords.java */ import java.util.*; class Ch9ExtractWords { private static final char BLANK = ' '; public static void main (String[] args) { Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); int index, numberOfCharacters, beginIdx, endIdx; String word, sentence; System.out.print(Input: ); Sentence = scanner.next( ); numberOfCharacters = sentence.length(); index = 0; while (index numberOfCharacters) { //ignore leading blank spaces while (index numberOfCharacters sentence.charAt(index) == BLANK) { index++; } beginIdx = index; //now locate the end of the word while (index numberOfCharacters sentence.charAt(index) != BLANK) { index++; } wu23399_ch09.qxd 12/15/06 20:05 Page 500
  • 528. endIdx = index; if (beginIdx != endIdx) { //another word is found, extract it from the //sentence and print it out word = sentence.substring( beginIdx, endIdx ); System.out.println(word); } } } } 9.2 Strings 501 Notice the signficance of the test if (beginIdx != endIdx) in the code. For what kinds of input sentences will the variables beginIdx and endIdx be equal? We’ll leave this as an exercise (see Exercise 16 at the end of the chapter). 1. Determine the output of the following code. a. String str = Programming; for (int i = 0; i 9; i+=2) { System.out.print( str.charAt( i ) ); } b. String str = World Wide Web; for (int i = 0; i 10; i ++ ) { if ( str.charAt(i) == 'W') { System.out.println( 'M' ); } else { System.out.print( str.charAt(i) ); } } 2. Write a loop that prints out a string in reverse. If the string is Hello, then the code outputs olleH. wu23399_ch09.qxd 12/15/06 20:05 Page 501
  • 529. 502 Chapter 9 Characters and Strings 3. Assume two String objects str1 and str2 are initialized as follows: String str1 = programming; String str2 = language; Determine the value of each of the following expressions if they are valid. If they are not valid, state the reason why. a. str1.compareTo( str2 ) b. str2.compareTo( str2 ) c. str2.substring( 1, 1 ) d. str2.substring( 0, 7 ) e. str2.charAt( 11 ) f. str1.length( ) + str2.length( ) 4. What is the difference between the two String methods equals and equalsIgnoreCase? 9.3 Pattern Matching and Regular Expression One sample code from Section 9.2 searched for the word Java in a given string. This sample code illustrated a very simplified version of a well-known problem called pattern matching. Word processor features such as finding a text and replacing a text with another text are two specialized cases of a pattern-matching problem. The matches Method Let’s begin with the matches method from the String class. In its simplest form, it looks very similar to the equals method. For example, given a string str, the two statements str.equals(Hello); str.matches(Hello); both evaluate to true if str is the string Hello. However, they are not truly equivalent, because, unlike equals, the argument to the matches method can be a pattern, a fea- ture that brings great flexibility and power to the matches method. Suppose we assign a three-digit code to all incoming students. The first digit represents the major, and 5 stands for the computer science major. The second digit represents the home state: 1 is for in-state students, 2 is for out-of-state students, and 3 is for foreign students. And the third digit represents the residence of the student. On-campus dormitories are represented by digits from 1 through 7. Students living off campus are represented by digit 8. For example, the valid encodings for students majoring in computer science and living off campus are 518, 528, and 538. The valid three-digit code for computer science majors living in one of the on-campus dormitories can be expressed succinctly as 5[123][1-7] pattern matching wu23399_ch09.qxd 12/15/06 20:05 Page 502
  • 530. and here’s how we interpret the pattern: The pattern is called a regular expression that allows us to denote a large (often in- finite) set of words succinctly. The “word” is composed of any sequence of symbols and is not limited to alphabets. The brackets [ ] are used here to represent choices, so [123] means 1, 2, or 3. We can use the notation for alphabets also. For example, [aBc] means a, B, or c. Notice the notation is case-sensitive. The hyphen in the brack- ets shows the range, so [1-7] means any digit from 1 to 7. If we want to allow any lowercase letter, then the regular expression will be [a-z]. The hat symbol ^ is used for negation. For example, [^abc] means any character except a, b, or c. Notice that this expression does not restrict the character to lowercase letters; it can be any character including digits and symbols. To refer to all lowercase letters except a, b, or c, the correct expression is [a-z[^abc]]. The double ampersand represents an intersection. Here are more examples: It must be 1, 2, or 3. It must be any digit from 1 to 7. It must be 5 for the computer science majors. first digit second digit third digit 5 [123] [1–7] 9.3 Pattern Matching and Regular Expression 503 regular expression Expression Description [013] A single digit 0,1,or 3. [0–9][0–9] Any two-digit number from 00 to 99. A[0–4]b[05] A string that consists of four characters.The first character is A.The second character is 0,1,2,3,or 4. The third character is b.And the last character is either 0 or 5. [0–9[ˆ4567]] A single digit that is 0,1,2,3,8,or 9. [a–z0–9] A single character that is either a lowercase letter or a digit. We can use repetition symbols * or + to designate a sequence of unbounded length. The symbol * means 0 or more times, and the symbol + means 1 or more times. Let’s try an example using a repetition symbol. Remember the definition for a valid Java identifier? We define it as a seqence of alphanumeric characters, underscores, and dollar signs, with the first character being an alphabet. In regular expression, we can state this definition as [a-zA-Z][a-zA-Z0-9_$]* wu23399_ch09.qxd 12/15/06 20:05 Page 503
  • 531. Let’s write a short program that will input a word and determine whether it is a valid Java identifier. The program stops when the word entered is STOP. Here’s the program: 504 Chapter 9 Characters and Strings /* Chapter 9 Sample Program: Checks whether the input string is a valid identifier. File: Ch9MatchJavaIdentifier.java */ import java.util.*; class Ch9MatchJavaIdentifier { private static final String STOP = STOP; private static final String VALID = Valid Java identifier; private static final String INVALID = Not a valid Java identifier; private static final String VALID_IDENTIFIER_PATTERN = [a-zA-Z][a-zA-Z0-9_$]*; public static void main (String[] args) { Scanner scanner = new Scanner (System.in); String str, reply; while (true) { System.out.print (Identifier: ); str = scanner.next( ); if (str.equals(STOP)) break; if (str.matches(VALID_IDENTIFIER_PATTERN)) { reply = VALID; } else { reply = INVALID; } System.out.println(str + : + reply + n); } } } It is also possible to designate a sequence of fixed length. For example, to spec- ify four-digit numbers, we write [0-9]{4}. The number in the braces { and } denotes the number of repetitions. We can specify the minimum and maximum numbers of wu23399_ch09.qxd 1/11/07 11:50 Page 504
  • 532. repetitions also. Here are the rules: Here’s an example of using a sequence of fixed length. Suppose we want to determine whether the input string represents a valid phone number that follows the pattern of xxx-xxx-xxxx where x is a single digit from 0 through 9. The following is a program that inputs a string continually and replies whether the input string conforms to the pattern. The program terminates when a single digit 0 is entered. Structurally this program is identical to the Ch9MatchJavaIdentifier class. Here’s the program: Expression Description X{N} Repeat X exactly N times,where X is a regular expression for a single character. X{N,} Repeat X at least N times. X{N,M} Repeat X at least N but no more than M times. 9.3 Pattern Matching and Regular Expression 505 /* Chapter 9 Sample Program: Checks whether the input string conforms to the phone number pattern xxx-xxx-xxxx. File: Ch9MatchPhoneNumber.java */ import java.util.*; class Ch9MatchPhoneNumber { private static final String STOP = 0; private static final String VALID = Valid phone number; private static final String INVALID = Not a valid phone number; private static final String VALID_PHONE_PATTERN = [0-9]{3}-[0-9]{3}-[0-9]{4}; public static void main (String[] args) { Scanner scanner = new Scanner (System.in); String phoneStr, reply; while (true) { System.out.print (Phone#: ); phoneStr = scanner.next( ); wu23399_ch09.qxd 12/15/06 20:05 Page 505
  • 533. Suppose, with the proliferation of cell phones, the number of digits used for a prefix increases from three to four in major cities. (In fact, Tokyo now uses a four- digit prefix. Phenomenal growth in the use of fax machines in both offices and homes caused the increase from three to four digits.) The valid format for phone numbers then becomes xxx-xxx-xxxx or xxx-xxxx-xxxx This change can be handled effortlessly by defining VALID_PHONE_PATTERN as private static final String VALID_PHONE_PATTERN = [0-9]{3}-[0-9]{3,4}-[0-9]{4}; This is the power of regular expression and pattern-matching methods. All we need to do is to make one simple adjustment to the regular expression. No other changes are made to the program. Had we written the program without using the pattern-matching technique (i.e., written the program using repetition control to test the first to the last character individually), changing the code to handle both a three- digit and a four-digit prefix requires substantially greater effort. The period symbol (.) is used to match any character except a line terminator such as n or r. (By using the Pattern class, we can make it match a line terminator also. We discuss more details on the Pattern class later.) We can use the period sym- bol with the zero-or-more-times notation * to check if a given string contains a sequence of characters we are looking for. For example, suppose a String object document holds the content of some document, and we want to check if the phrase “zen of objects” is in it. We can do it as follows: String document; document = ...; //assign text to 'document' if (document.matches(.*zen of objects.*) { System.out.println(Found); 506 Chapter 9 Characters and Strings if (phoneStr.equals(STOP)) break; if (phoneStr.matches(VALID_PHONE_PATTERN)) { reply = VALID; } else { reply = INVALID; } System.out.println(phoneStr + : + reply + n); } } } wu23399_ch09.qxd 12/15/06 20:05 Page 506
  • 534. } else { System.out.println(Not found); } The brackets [ and ] are used for expressing a range of choices for a single character. If we need to express a range of choices for multiple characters, then we use the parentheses and the vertical bar. For example, if we search for the word maximum or minimum, we express the pattern as (max|min)imum Here are some more examples: The replaceAll Method Using the replaceAll method, we can replace all occurrences of a substring that matches a given regular expression with a given replacement string. For example, here’s how to replace all vowels in the string with the @ symbol: String originalText, modifiedText; originalText = ...; //assign string to 'originalText' modifiedText = originalText.replaceAll([aeiou], @); Notice the original text is unchanged. The replaceAll method returns a modified text as a separate string. Here are more examples: Expression Description [wb](ad|eed) Matches wad,weed,bad,and beed. (pro|anti)-OO? Matches pro-OOP and anti-OOP. (AZ|CA|CO)[0–9]{4} Matches AZxxxx,CAxxxx,and COxxxx, where x is a single digit. 9.3 Pattern Matching and Regular Expression 507 Expression Description str.replaceAll(OOP, Replace all occurrences of OOP with object-oriented programming) object-oriented programming. str.replaceAll( Replace all social security numbers [0-9]{3}-[0-9]{2}-[0-9]{4}, with xxx-xx-xxxx. xxx-xx-xxxx) str.replaceAll(o{2,}, oo) Replace all occurrences of a sequence that has two or more of letter o with oo. wu23399_ch09.qxd 12/15/06 20:05 Page 507
  • 535. If we want to match only the whole word, we have to use the b symbol to des- ignate the word boundary. Suppose we write str.replaceAll(temp, temporary); expecting to replace all occurrences of the abbreviated word temp by temporary. We will get a surprising result. All occurrences of the sequence of characters temp will be replaced; so, for example, words such as attempt or tempting would be replaced by attemporaryt or temporaryting, respectively. To designate the sequence temp as a whole word, we place the word boundary symbol b in the front and end of the sequence. str.replaceAll(btempb, temporary); Notice the use of two backslashes. The symbol we use in the regular expres- sion is b. However, we must write this regular expression in a String representation. And remember that the backslash symbol in a string represents a control character such as n, t, and r. To specify the regular expression symbol with a backslash, we must use additional backslash, so the system will not interpret it as some kind of control character. The regular expression we want here is btempb To put it in a String representation, we write btempb Here are the common backslash symbols used in regular expressions: 508 Chapter 9 Characters and Strings String Expression Representation Description d d A single digit.Equivalent to [0–9]. D D A single nondigit.Equivalent to [^0–9]. s s A white space character,such as space, tab,new line,etc. S S A non-white-space character. w w A word character.Equivalent to [a–zA–Z_0–9]. W W A nonword character. b b A word boundary (such as a white space and punctuation mark). B B A nonword boundary. We also use the backslash if we want to search for a command character. For example, the plus symbol designates one or more repetitions. If we want to search for the plus symbol in the text, we use the backslash as + and to express it as a wu23399_ch09.qxd 12/15/06 20:05 Page 508
  • 536. string, we write “+”. Here’s an example. To replace all occurrences of C and C++ (not necessarily a whole word) with Java, we write str.replaceAll((C|C++), Java); 9.4 The Pattern and Matcher Classes 509 1. Describe the string that the following regular expressions match. a. a*b b. b[aiu]d c. [Oo]bject(s| ) 2. Write a regular expression for a state vehicle license number whose format is a single capital letter, followed by three digits and four lowercase letters. 3. Which of the following regular expressions are invalid? a. (a-z)*+ b. [a|ab]xyz c. abe-14 d. [a-z^a^b] e. [//one]two 9.4 The Pattern and Matcher Classes The matches and replaceAll methods of the String class are shorthand for using the Pattern and Matcher classes from the java.util.regex package. We will describe how to use these two classes for more efficient pattern matching. The statement str.matches(regex); where str and regex are String objects is equivalent to Pattern.matches(regex, str); which in turn is equivalent to Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(str); matcher.matches(); Similarly, the statement str.replaceAll(regex, replacement); where replacement is a replacement text is equivalent to Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(str); matcher.replaceAll(replacement); wu23399_ch09.qxd 12/15/06 20:05 Page 509
  • 537. Explicit creation of Pattern and Matcher objects gives us more options and greater efficiency. We specify regular expressions as strings, but for the system to actually carry out the pattern-matching operation, the stated regular expression must first be converted to an internal format. This is done by the compile method of the Pattern class. When we use the matches method of the String or Pattern class, this conversion into the internal format is carried out every time the matches method is executed. So if we use the same pattern multiple times, then it is more efficient to convert just once, instead of repeating the same conversion, as was the case for the Ch9MatchJavaIdentifier and Ch9MatchPhoneNumber classes. The following is Ch9MatchJavaIdentifierPM, a more efficient version of Ch9MatchJavaIdentifier: 510 Chapter 9 Characters and Strings /* Chapter 9 Sample Program: Checks whether the input string is a valid identifier. This version uses the Matcher and Pattern classes. File: Ch9MatchJavaIdentifierPM.java */ import java.util.*; import java.util.regex.*; class Ch9MatchJavaIdentifierPM { private static final String STOP = STOP; private static final String VALID = Valid Java identifier; private static final String INVALID = Not a valid Java identifier; private static final String VALID_IDENTIFIER_PATTERN = [a-zA-Z][a-zA-Z0-9_$]*; public static void main (String[] args) { Scanner scanner = new Scanner(System.in); String str, reply; Matcher matcher; Pattern pattern = Pattern.compile(VALID_IDENTIFIER_PATTERN); while (true) { System.out.print(Identifier: ); str = Scanner.next(); if (str.equals(STOP)) break; matcher = pattern.matcher(str); if (matcher.matches()) { reply = VALID; wu23399_ch09.qxd 12/15/06 20:05 Page 510
  • 538. } else { reply = INVALID; } System.out.println(str + : + reply + n); } } } 9.4 The Pattern and Matcher Classes 511 We have a number of options when the Pattern compiles into an internal format. For example, by default, the period symbol does not match the line terminator char- acter. We can override this default by passing DOTALL as the second argument as Pattern pattern = Pattern.compile(regex, Pattern.DOTALL); To enable case-insensitive matching, we pass the CASE_INSENSITIVE constant. The find method is another powerful method of the Matcher class. This method searches for the next sequence in a string that matches the pattern. The method returns true if the patten is found. We can call the method repeatedly until it returns false to find all matches. Here’s an example that counts the number of times the word java occurs in a given document. We will search for the word in a case-insensitive manner. /* Chapter 9 Sample Program: Count the number of times the word 'java' occurs in input sentence using pattern matching. File: Ch9CountJavaPM.java */ import java.util.*; import java.util.regex.*; class Ch9CountJavaPM { public static void main (String[] args) { Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); String document; int javaCount; Matcher matcher; Pattern pattern = Pattern.compile(java, Pattern.CASE_INSENSITIVE); wu23399_ch09.qxd 12/15/06 20:05 Page 511
  • 539. System.out.println(Sentence: ); document = scanner.next(); javaCount = 0; matcher = pattern.matcher(document); while (matcher.find()) { javaCount++; } System.out.println(The word 'java' occurred + javaCount + times.); } } 512 Chapter 9 Characters and Strings When a matcher finds a matching sequence of characters, we can query the location of the sequence by using the start and end methods. The start method returns the position in the string where the first character of the pattern is found, and the end method returns the value 1 more than the position in the string where the last character of the pattern is found. Here’s the code that prints out the matching sequences and their locations in the string when searching for the word java in a case-insensitive manner. /* Chapter 9 Sample Program: Displays the positions of the word 'java' in a given string using pattern-matching technique. File: Ch9LocateJavaPM.java */ import javax.swing.*; import java.util.regex.*; class Ch9LocateJavaPM { public static void main (String[] args) { Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); String document; Matcher matcher; Pattern pattern = Pattern.compile(java, Pattern.CASE_INSENSITIVE); wu23399_ch09.qxd 12/15/06 20:05 Page 512
  • 540. System.out.println(Sentence: ); document = scanner.next(); matcher = pattern.matcher(document); while (matcher.find()) { System.out.println(document.substring(matcher.start(), matcher.end()) + found at position + matcher.start()); } } } 9.5 Comparing Strings 513 1. Replace the following statements with the equivalent ones using the Pattern and Matcher classes. a. str.replaceAll(1, one); b. str.matches(alpha); 2. Using the find method of the Matcher class, check if the given string document contains the whole word Java. 9.5 Comparing Strings We already discussed how objects are compared in Chapter 5. The same rule applies for the string, but we have to be careful in certain situations because of the differ- ence in the way a new String object is created. First, we will review how the objects are compared. The difference between String word1, word2; ... if ( word1 == word2 ) ... and if ( word1.equals(word2) ) ... is illustrated in Figure 9.2. The equality test == is true if the contents of variables are the same. For a primitive data type, the contents are values themselves; but for a ref- erence data type, the contents are addresses. So for a reference data type, the equality test is true if both variables refer to the same object, because they both contain the same address. The equals method, on the other hand, is true if the String objects to which the two variables refer contain the same string value. To distinguish the two types of comparisons, we will use the term equivalence test for the equals method. versus equals equivalence test wu23399_ch09.qxd 12/15/06 20:05 Page 513
  • 541. As long as we create a new String object as String str = new String(Java); using the new operator, the rule for comparing objects applies to comparing strings. However, when the new operator is not used, for example, in String str = Java; we have to be careful. Figure 9.3 shows the difference in assigning a String object to a variable. If we do not use the new operator, then string data are treated as if they 514 Chapter 9 Characters and Strings word1 == word2 is true word1.equals( word2 ) is true :String word1 Case A: Referring to the same object. Java word2 Note: If x ⴝⴝ y is true, then x.equals(y) is also true. The reverse is not always true. word1 == word2 is false word1.equals( word2 ) is true :String word1 Case B: Referring to different objects having identical string values. Java :String Java word2 word1 == word2 is false word1.equals( word2 ) is false :String word1 Case C: Referring to different objects having different string values. Bali :String Java word2 Figure 9.2 The difference between the equality test and the equals method. wu23399_ch09.qxd 12/15/06 20:05 Page 514
  • 542. are primitive data type. When we use the same literal String constants in a program, there will be exactly one String object. 9.6 StringBuffer and StringBuilder 515 String word1, word2; word1 = new String(Java); word2 = new String(Java); Whenever the new operator is used, there will be a new object. String word1, word2; word1 = Java; word2 = Java; Literal string constant such as “Java” will always refer to the one object. :String word1 Java word2 :String word1 Java :String Java word2 Figure 9.3 Difference between using and not using the new operator for String. 1. Show the state of memory after the following statements are executed. String str1, str2, str3; str1 = Jasmine; str2 = Oolong; str3 = str2; str2 = str1; 9.6 StringBuffer and StringBuilder A String object is immutable, which means that once a String object is created, we cannot change it. In other words, we can read individual characters in a string, but we cannot add, delete, or modify characters of a String object. Remember that the methods of the String class, such as replaceAll and substring, do not modify the original string; they return a new string. Java adopts this immutability restriction to implement an efficient memory allocation scheme for managing String objects. The immutability is the reason why we can treat the string data much as a primitive data type. wu23399_ch09.qxd 12/15/06 20:05 Page 515
  • 543. Creating a new string from the old one will work for most cases, but some times manipulating the content of a string directly is more convenient. When we need to compose a long string from a number of words, for example, being able to manipulate the content of a string directly is much more convenient than creating a new copy of a string. String manipulation here means operations such as replacing a character, appending a string with another string, deleting a portion of a string, and so forth. If we need to manipulate the content of a string directly, we must use either the StringBuffer or the StringBuilder class. Here’s a simple example of modifying the string Java to Diva using a StringBuffer object: StringBuffer word = new StringBuffer( Java ); word.setCharAt(0, 'D'); word.setCharAt(1, 'i'); Notice that no new string is created, the original string Java is modified. Also, we must use the new method to create a StringBuffer object. The StringBuffer and StringBuilder classes behave exactly the same (i.e., they support the same set of public methods), but the StringBuilder class in general has a better performance. The StringBuilder class is new to Java 2 SDK version 1.5, so it cannot be used with the older versions of Java SDK. There are advanced cases where you have to use the StringBuffer class, but for the sample string processing programs in this book, we can use either one of them. Of course, to use the String- Builder class, we must be using version 1.5 SDK. We can also continue to use the StringBuffer class with version 1.5. Because the StringBuffer class can be used with all versions of Java SDK, and the string processing performance in not our major concern here, we will be using the StringBuffer class exclusively in this book. If the string processing performance is a concern, then all we have to do is to replace all occurrences of the word String- Buffer to StringBuilder in the program and run it with version 1.5 SDK. Let’s look at some examples using StringBuffer objects. The first example reads a sentence and replaces all vowels in the sentence with the character X. 516 Chapter 9 Characters and Strings string manipulation StringBuffer /* Chapter 9 Sample Program: Replace every vowel in a given sentence with 'X' using StringBuffer. File: Ch9ReplaceVowelsWithX.java */ import java.util.*; class Ch9ReplaceVowelsWithX { public static void main (String[] args) { Scanner scanner = new Scanner(System.in); scanner.useDelimiter(System.getProperty(line.separator)); wu23399_ch09.qxd 12/15/06 20:05 Page 516
  • 544. Bad Version StringBuffer tempStringBuffer; String inSentence; int numberOfCharacters; char letter; System.out.println(Sentence: ); inSentence = scanner.next(); tempStringBuffer = new StringBuffer(inSentence); numberOfCharacters = tempStringBuffer.length(); for (int index = 0; index numberOfCharacters; index++) { letter = tempStringBuffer.charAt(index); if (letter == 'a' || letter == 'A' || letter == 'e' || letter == 'E' || letter == 'i' || letter == 'I' || letter == 'o' || letter == 'O' || letter == 'u' || letter == 'U' ) { tempStringBuffer.setCharAt(index,'X'); } } System.out.println(Input: + inSentence); System.out.println(Output: + tempStringBuffer); } } 9.6 StringBuffer and StringBuilder 517 Notice how the input routine is done. We are reading in a String object and converting it to a StringBuffer object, because we cannot simply assign a String object to a StringBuffer variable. For example, the following code is invalid: StringBuffer strBuffer = scanner.next(); We are required to create a StringBuffer object from a String object as in String str = Hello; StringBuffer strBuf = new StringBuffer( str ); We cannot input StringBuffer objects. We have to input String objects and convert them to StringBuffer objects. wu23399_ch09.qxd 12/15/06 20:05 Page 517
  • 545. Our next example constructs a new sentence from input words that have an even number of letters. The program stops when the word STOP is read. Let’s begin with the pseudocode: set tempStringBuffer to empty string; repeat = true; while ( repeat ) { read in next word; if (word is STOP) { repeat = false; } else if (word has even number of letters) { append word to tempStringBuffer; } } And here’s the actual code: 518 Chapter 9 Characters and Strings /* Chapter 9 Sample Program: Constructs a new sentence from input words that have an even number of letters. File: Ch9EvenLetterWords.java */ import javax.swing.*; class Ch9EvenLetterWords { public static void main (String[] args) { Scanner scanner = new Scanner(System.in); boolean repeat = true; String word; StringBuffer tempStringBuffer = new StringBuffer(); while (repeat) { System.out.print(Next word: ); word = scanner.next( ); if (word.equals(STOP)) { repeat = false; } else if (word.length() % 2 == 0) { tempStringBuffer.append(word + ); } } Create StringBuffer object with an empty string. Append word and a space to tempStringBuffer. wu23399_ch09.qxd 12/15/06 20:05 Page 518
  • 546. System.out.println(Output: + tempStringBuffer); } } 9.6 StringBuffer and StringBuilder 519 We use the append method to append a String or a StringBuffer object to the end of a StringBuffer object. The method append also can take an argument of the primitive data type. For example, all the following statements are valid: int i = 12; float x = 12.4f; char ch = 'W'; StringBuffer str = new StringBuffer(); str.append(i); str.append(x); str.append(ch); Any primitive data type argument is converted to a string before it is appended to a StringBuffer object. Notice that we can write the second example using only String objects. Here’s how: boolean repeat = true; String word, newSentence; newSentence = ; //empty string while (repeat) { System.out.print(Next word: ); word = scanner.next(); if (word.equals(STOP)) { repeat = false; } else if (word.length() % 2 == 0) { newSentence = newSentence + word; //string concatenation } } Although this code does not explicitly use any StringBuffer object, the Java compiler may use StringBuffer when compiling the string concatenation operator. For example, the expression newSentence + word can be compiled as if the expression were new StringBuffer().append(word).toString() wu23399_ch09.qxd 12/15/06 20:05 Page 519
  • 547. Using the append method of StringBuffer is preferable to using the string concate- nation operator + because we can avoid creating temporary string objects by using StringBuffer. In addition to appending a string at the end of StringBuffer, we can insert a string at a specified position by using the insert method. The syntax for this method is StringBuffer . insert ( insertIndex, value ) ; where insertIndex must be greater than or equal to 0 and less than or equal to the length of StringBuffer and the value is an object or a value of the primitive data type. For example, to change the string Java is great to Java is really great we can execute StringBuffer str = new StringBuffer(Java is great); str.insert(8, really ); 520 Chapter 9 Characters and Strings 1. Determine the value of str after the following statements are executed. a. StringBuffer str = new StringBuffer( Caffeine ); str.insert(0, Dr. ); b. String str = Caffeine; StringBuffer str1 = new StringBuffer( str.substring(1, 3) ); str1.append('e'); str = De + str1; c. String str = Caffeine; StringBuffer str = new StringBuffer( str.substring(4, 8); str1.insert ( 3,'f'); str = De + str1 2. Assume a String object str is assigned as a string value. Write a code segment to replace all occurrences of lowercase vowels in a given string to the letter C by using String and StringBuffer objects. 3. Find the errors in the following code. String str = Caffeine; StringBuffer str1 = str.substring(1, 3); str1.append('e'); System.out(str1); str1 = str1 + str; wu23399_ch09.qxd 12/15/06 20:05 Page 520
  • 548. Sample Development Building Word Concordance One technique to analyze a historical document or literature is to track word occurrences. A basic form of word concordance is a list of all words in a document and the number of times each word appears in the document. Word concordance is useful in revealing the writing style of an author.For example,given a word concordance of a document,we can scan the list and count the numbers of nouns, verbs, prepositions, and so forth. If the ratios of these grammatical elements differ significantly between the two documents, there is a high probability that they are not written by the same person. Another appli- cation of word concordance is seen in the indexing of a document, which, for each word, lists the page numbers or line numbers where it appears in the document. In this sample development, we will build a word concordance of a given document, utilizing the string-processing technique we learned in this chapter. 9.7 Sample Development 521 9.7 Sample Development word con- cordance One of the most popular search engine websites on the Internet today is Google (www.google.com). At the core of their innovative technology is a concordance of all Web pages on the Internet. Every month the company’s Web crawler software visits 3 billion (and steadily growing) Web pages, and from these visits, a concordance is built. When the user enters a query, the Google servers search the concordance for a list of matching Web pages and return the list in the order of relevance. Problem Statement Write an application that will build a word concordance of a document. The output from the application is an alphabetical list of all words in the given document and the number of times they occur in the document. The docu- ments are a text file (contents of the file are ASCII characters), and the output of the program is saved as an ASCII file also. Overall Plan As usual, let’s begin the program development by first identifying the major tasks of the program. The first task is to get a text document from a designated file. We will use a helper class called FileManager to do this task.File processing techniques to implement the FileManager class will be presented in Chapter 12. The whole content of an ASCII file is represented in the program as a single String object. Using a pattern-matching tech- nique,we extract individual words from the document.For each distinct word in the doc- ument, we associate a counter and increment it every time the word is repeated. We will use the second helper class called WordList for maintaining a word list.An entry in this list has two components—a word and how many times this word occurs in the document. wu23399_ch09.qxd 12/15/06 20:05 Page 521
  • 549. 9.7 Sample Development—continued 522 Chapter 9 Characters and Strings A WordList object can handle an unbounded number of entries. Entries in the list are arranged in alphabetical order. We will learn how to implement the WordList class in Chapter 10. We can express the program logic in pseudocode as while ( the user wants to process another file ) { Task 1: read the file; Task 2: build the word list; Task 3: save the word list to a file; } Let’s look at the three tasks and determine objects that will be responsible for handling the tasks. For the first task, we will use the helper class FileManager. For the second task of building a word list,we will define the Ch9WordConcordance class,whose instance will use the Pattern and Matcher classes for word extraction, and another helper class WordList for maintaining the word list. The last task of saving the result is done by the FileManager class also. Finally, we will define a top-level control object that manages all other objects.We will call this class Ch9WordConcordanceMain. This will be our instantiable main class. Here’s our working design document: program tasks program classes Design Document: Ch9WordConcordanceMain Class Purpose Ch9WordConcordanceMain The instantiable main class of the program that implements the top-level program control. Ch9WordConcordance The key class of the program.An instance of this class manages other objects to build the word list. FileManager A helper class for opening a file and saving the result to a file.Details of this class can be found in Chapter 12. WordList Another helper class for maintaining a word list.Details of this class can be found in Chapter 10. Pattern/Matcher Classes for pattern-matching operations. Figure 9.4 is the working program diagram. wu23399_ch09.qxd 12/15/06 20:05 Page 522
  • 550. We will implement this program in four major steps: 1. Start with a program skeleton.Define the main class with data members.To test the main class,we will also define a skeleton Ch9WordConcordance class with just a default constructor. 2. Add code to open a file and save the result.Extend the step 1 classes as necessary. 3. Complete the implementation of the Ch9WordConcordance class. 4. Finalize the code by removing temporary statements and tying up loose ends. 9.7 Sample Development 523 Figure 9.4 The program diagram for the Ch9WordConcordanceMain program.Base system classes such as String and JOptionPane are not shown. WordList Ch9Word Concordance Pattern FileManager Matcher Ch9Word ConcordanceMain A helper class provided to us A class we implement System classes In lieu of the Pattern and Matcher classes, we could use the String- Tokenizer class. This class is fairly straightforward to use if the white space (tab, return, blank, etc.) is a word delimiter.However, using this class becomes a little more complicated if we need to include punctuation marks and others as a word delimiter also. Overall, the Pattern and Matcher classes are more powerful and useful in many types of applications than the String- Tokenizer class. development steps wu23399_ch09.qxd 12/15/06 20:05 Page 523
  • 551. 9.7 Sample Development—continued Step 1 Development: Skeleton The design of Ch9WordConcordanceMain is straightforward,as its structure is very sim- ilar to that of other main classes.We will make this an instantiable main class and define the start method that implements the top-level control logic. We will define a default constructor to create instances of other classes. A skeleton Ch9WordConcordance class is also defined in this step so we can compile and run the main class. The skeleton Ch9WordConcordance class only has an empty default constructor.The working design document for the Ch9WordConcordanceMain class is as follows: 524 Chapter 9 Characters and Strings step 1 design step 1 code Design Document: The Ch9WordConcordanceMain Class Method Visibility Purpose constructor public Creates the instances of other classes in the program. start private Implements the top-level control logic of the program. For the skeleton, the start method loops (doing nothing inside the loop in this step) until the user selects No on the confirmation dialog.Here’s the skeleton: /* Chapter 9 Sample Development: Word Concordance File: Step1/Ch9WordConcordanceMain.java */ import java.util.*; class Ch9WordConcordanceMain { private static enum Response {YES, NO} private FileManager fileManager; private Ch9WordConcordance builder; private Scanner scanner; //---------------------------------- // Main method //---------------------------------- public static void main(String[] args) { Ch9WordConcordanceMain main = new Ch9WordConcordanceMain(); main.start(); } wu23399_ch09.qxd 12/15/06 20:05 Page 524
  • 552. public Ch9WordConcordanceMain() { fileManager = new FileManager( ); builder = new Ch9WordConcordance( ); scanner = new Scanner(System.in); } private void start( ) { Response userReply; while (true) { userReply = prompt(Run the program?); if (userReply == Response.NO) { break; } } System.out.println(Thank you for using the program. Good-Bye); } private Response prompt(String question) { String input; Response response = Response.NO; System.out.print(question + (Yes - y; No - n): ); input = scanner.next(); if (input.equals(Y) || input.equals(y)) { response = Response.YES; } return response; } } 9.7 Sample Development 525 class Ch9WordConcordance { public Ch9WordConcordance() { } } The skeleton Ch9WordConcordance class has only an empty default constructor. Here’s the skeleton class: wu23399_ch09.qxd 12/15/06 20:05 Page 525
  • 553. 9.7 Sample Development—continued We run the program and verify that the constructor is executed correctly, and the repetition control in the start method works as expected. Step 2 Development: Open and Save Files In the second development step,we add routines to handle input and output.The tasks of opening and saving a file are delegated to the service class FileManager.We will learn the implementation details of the FileManager class in Chapter 12. Our responsibility right now is to use the class correctly.The class provides two key methods:one to open a file and another to save a file.So that we can create and view the content easily,the FileManager class deals only with text files.To open a text file, we call its openFile method.There are two versions.With the first version,we pass the filename.For example,the code FileManager fm = new FileManager(); String doc = ...; //assign string data fm.saveFile(output1.txt, doc); will save the string data doc to a file named output1.txt.With the second version,we will let the end user select a file,using the standard file dialog.A sample file dialog is shown in Figure 9.5.With the second version,we pass only the string data to be saved as fm.saveFile(doc); When there’s an error in saving a file,an IOException is thrown. To open a text file, we use one of the two versions of the openFile method. The distinction is identical to the one for the saveFile methods.The first version requires the 526 Chapter 9 Characters and Strings step 1 test step 2 design Figure 9.5 A sample file dialog for opening a file. wu23399_ch09.qxd 12/15/06 20:05 Page 526
  • 554. filename to open.The second version allows the end user to select a file to save the data, so we pass no parameter. The openFile method will throw a FileNotFoundException when the designated file cannot be found and an IOException when the designated file cannot be opened correctly. Here’s the summary of the FileManager class: 9.7 Sample Development 527 Public Methods of FileManager public String openFile(String filename) throws FileNotFoundException, IOException Opens the text file filename and returns the content as a String. public String openFile( ) throws FileNotFoundException, IOException Opens the text file selected by the end user,using the standard file open dialog, and returns the content as a String. public String saveFile(String filename, String data) throws IOException Save the string data to filename. public String saveFile(String data) throws IOException Saves the string data to a file selected by the end user,using the standard file save dialog. We modify the start method to open a file, create a word concordance, and then save the generated word concordance to a file.The method is defined as follows: private void start( ) { Response userReply; String document, wordList; while (true) { userReply = prompt(Run the program?); if (userReply == Response.NO) { break; } document = inputFile(); //open file wordList = build(document); //build concordance saveFile(wordList); //save the generated concordance } ... //'Good-bye' message dialog } Added portion wu23399_ch09.qxd 12/15/06 20:05 Page 527
  • 555. 9.7 Sample Development—continued The inputFile method is defined as follows: private String inputFile( ) { String doc = ; try { doc = fileManager.openFile( ); } catch ( FileNotFoundException e) { System.out.println(File not found.); } catch ( IOException e) { System.out.println(Error in opening file: + e.getMessage()); } System.out.println(Input Document:n + doc); //TEMP return doc; } with a temporary output to verify the input routine. Because the openFile method of FileManager throws exceptions,we handle them here with the try-catch block. The saveFile method is defined as follows: private void saveFile(String list) { try { fileManager.saveFile(list); } catch (IOException e) { System.out.println(Error in saving file: + e.getMessage()); } } The method is very simple as the hard work of actually saving the text data is done by our FileManager helper object. Finally, the build method is defined as private String build(String document) { String concordance; concordance = builder.build(document); return concordance; } 528 Chapter 9 Characters and Strings wu23399_ch09.qxd 12/15/06 20:05 Page 528
  • 556. The Ch9WordConcordanceMain class is now complete.To run and test this class, we will define a stub build method for the Ch9WordConcordance class.The method is temporarily defined as public String build(String document) { //TEMP String list = one 14ntwo 3nthree 3nfour 5nfive 92n; return list; //TEMP } We will implement the method fully in the next step. Here’s the final Ch9WordConcordanceMain class: 9.7 Sample Development 529 step 2 code /* Chapter 9 Sample Development: Word Concordance File: Step2/Ch9WordConcordanceMain.java */ import java.io.*; import java.util.*; class Ch9WordConcordanceMain { ... private String build(String document) { String concordance; concordance = builder.build(document); return concordance; } private String inputFile( ) { String doc = ; try { doc = fileManager.openFile( ); } catch (FileNotFoundException e) { System.out.println(File not found.); } catch (IOException e) { System.out.println(Error in opening file: + e.getMessage()); } build inputFile wu23399_ch09.qxd 12/15/06 20:05 Page 529
  • 557. 9.7 Sample Development—continued System.out.println(Input Document:n + doc); //TEMP return doc; } private void saveFile(String list) { try { fileManager.saveFile(list); } catch (IOException e) { System.out.println(Error in saving file: + e.getMessage()); } } private void start( ) { while (true) { ... document = inputFile(); wordList = build(document); saveFile(wordList); } ... } } 530 Chapter 9 Characters and Strings start saveFile The temporary Ch9WordConcordance class now has the stub build method: class Ch9WordConcordance { ... public String build(String document) { //TEMP String list = one 14ntwo 3nthree 3nfour 5nfive 92n; return list; //TEMP } } wu23399_ch09.qxd 12/15/06 20:05 Page 530
  • 558. We are ready to run the program.The step 2 directory contains several sample input files. We will open them and verify the file contents are read correctly by checking the temporary echo print output to System.out. To verify the output routine, we save to the output (the temporary output created by the build method of Ch9WordConcordance) and verify its content.Since the output is a text file,we can use any word processor or text editor to view its contents.(Note: If we use NotePad on the Windows platform to view the file,it may not appear correctly.See the box below on how to avoid this problem.) 9.7 Sample Development 531 step 2 test step 3 design The control characters used for a line separator are not the same for each plat- form (Windows,Mac,Unix, etc.) .One platform may use n for a line separator while another platform may use rn for a line separator. Even on the same platform, different software may not interpret the control characters in the same way.To make our Java code work correctly across all platforms, we do, for example, String newline = System.getProperties().getProperty(line.separator); String output = line 1 + newline + line 2 + newline; instead of String output = line 1nline 2n; Step 3 Development: Generate Word Concordance In the third development step, we finish the program by implementing the Ch9Word- Concordance class,specifically,its build method.Since we are using another helper class in this step, first we must find out how to use this helper class. The WordList class sup- ports the maintenance of a word list. Every time we extract a new word from the docu- ment, we enter this word into a word list. If the word is already in the list, its count is incremented by 1. If the word occurs for the first time in the document, then the word is added to the list with its count initialized to 1.When we are done processing the docu- ment,we can get the word concordance from a WordList by calling its getConcordance method.The method returns the list as a single String with each line containing a word and its count in the following format: 2 Chapter 1 Early 1 However 2 In 1 already 1 also 1 an wu23399_ch09.qxd 12/15/06 20:05 Page 531
  • 559. 9.7 Sample Development—continued 7 and 1 are 2 as 1 because Because a single WordList object handles multiple documents, there’s a method called reset to clear the word list before processing the next document. Here’s the method summary: 532 Chapter 9 Characters and Strings Public Methods of WordList public void add(String word) Increments the count for the given word.If the word is already in the list,its count is incremented by 1.If the word does not exist in the list,then it is added to the list with its count set to 1. public String getConcordance( ) Returns the word concordance in alphabetical order of words as a single string. Each line consists of a word and its count. public void reset( ) Clears the internal data structure so a new word list can be constructed. This method must be called every time before a new document is processed. The general idea behind the build method of the Ch9WordConcordance class is straightforward. We need to keep extracting a word from the document, and for every word found,we add it to the word list.Expressed in pseudocode,we have while (document has more words) { word = next word in the document; wordList.add(word); } String concordance = wordList.getConcordance(); The most difficult part here is how to extract words from a document. We can write our own homemade routine to extract words, based on the technique presented in Section 9.2. However, this is too much work to get the task done. Writing a code that detects various kinds of word terminators (in addition to space,punctuation mark,control characters such as tab, new line, etc., all satisfy as the word terminator) is not that easy. Conceptually, it is not that hard, but it can be quite tedious to iron out all the details. Instead, we can use the pattern-matching technique provided by the Pattern and Matcher classes for a reliable and efficient solution. The pattern for finding a word can be stated in a regular expression as w+ wu23399_ch09.qxd 12/15/06 20:05 Page 532
  • 560. Putting it in a string format results in w+ The Pattern and Matcher objects are thus created as Pattern pattern = Pattern.compile(w+); Matcher matcher = pattern.matcher(document); and the control loop to find and extract words is wordList.reset(); while (matcher.find( )) { wordList.add(document.substring(matcher.start(), matcher.end())); } Here’s the final Ch9WordConcordance class: 9.7 Sample Development 533 step 3 code /* Chapter 9 Sample Development: Word Concordance File: Step3/Ch9WordConcordance.java */ import java.util.regex.*; class Ch9WordConcordance { private static final String WORD = w+; private WordList wordList; private Pattern pattern; public Ch9WordConcordance() { wordList = new WordList(); pattern = Pattern.compile(WORD); //pattern is compiled only once } public String build(String document) { Matcher matcher = pattern.matcher(document); wordList.reset(); while (matcher.find()){ wordList.add(document.substring(matcher.start(), matcher.end())); } return wordList.getConcordance(); } } build wu23399_ch09.qxd 12/15/06 20:05 Page 533
  • 561. 9.7 Sample Development—continued Notice how short the class is, thanks to the power of pattern matching and the helper WordList class. We run the program against varying types of input text files. We can use a long document such as the term paper for the last term’s economy class (don’t forget to save it as a text file before testing). We should also use some specially created files for testing purposes.One file may contain only one word repeated 7 times,for example.Another file may contain no words at all. We verify that the program works correctly for all types of input files. Step 4 Development: Finalize As always, we finalize the program in the last step. We perform a critical review to find any inconsistency or error in the methods,any incomplete methods,places to add more comments, and so forth. In addition, we may consider possible extensions. One is an integrated user inter- face where the end user can view both the input document files and the output word list files. Another is the generation of different types of list. In the sample development, we count the number of occurrences of each word. Instead, we can generate a list of posi- tions where each word appears in the document. The WordList class itself needs to be modified for such extension. 534 Chapter 9 Characters and Strings step 3 test program review • The char data type represents a single character. • The char constant is denoted by a single quotation mark, for example, ‘a’. • The character coding scheme used widely today is ASCII (American Standard Code for Information Exchange). • Java uses Unicode, which is capable of representing characters of diverse languages. ASCII is compatible with Unicode. • A string is a sequence of characters, and in Java, strings are represented by String objects. • The Pattern and Matcher classes are introduced in Java 2 SDK 1.4. They provide support for pattern-matching applications. • Regular expression is used to represent a pattern to match (search) in a given text. • The String objects are immutable. Once they are created, they cannot be changed. • To manipulate mutable strings, use StringBuffer. S u m m a r y wu23399_ch09.qxd 12/15/06 20:05 Page 534
  • 562. • Strings are objects in Java, and the rules for comparing objects apply when comparing strings. • Only one String object is created for the same literal String constants. • The standard classes described or used in this chapter are String Pattern StringBuffer Matcher StringBuilder Exercises 535 K e y C o n c e p t s characters strings string processing regular expression pattern matching character encoding String comparison E x e r c i s e s 1. What is the difference between ’a’ and ”a”? 2. Discuss the difference between str = str + word; //string concatenation and tempStringBuffer.append(word) where str is a String object and tempStringBuffer is a StringBuffer object. 3. Show that if x and y are String objects and x == y is true, then x.equals(y) is also true, but the reverse is not necessarily true. 4. What will be the output from the following code? StringBuffer word1, word2; word1 = new StringBuffer(Lisa); word2 = word1; word2.insert(0, Mona ); System.out.println(word1); 5. Show the state of memory after the execution of each statement in the following code. String word1, word2; word1 = Hello; word2 = word1; word1 = Java; wu23399_ch09.qxd 12/15/06 20:05 Page 535
  • 563. 6. Using a state-of-memory diagram, illustrate the difference between a null string and an empty string—a string that has no characters in it. Show the state-of-memory diagram for the following code. Variable word1 is a null string, while word2 is an empty string. String word1, word2; word1 = null; word2 = ; 7. Draw a state-of-memory diagram for each of the following groups of statements. String word1, word2; String word1, word2; word1 = French Roast; word1 = French Roast; word2 = word1; word2 = French Roast; 8. Write an application that reads in a character and displays the character’s ASCII. The getText method of the JTextField class returns a String object, so you need to extract a char value, as in String inputString = inputField.getText(); char character = inputString.charAt(0); Display an error message if more than one character is entered. 9. Write a method that returns the number of uppercase letters in a String object passed to the method as an argument. Use the class method isUpperCase of the Character class, which returns true if the passed parameter of type char is an uppercase letter. You need to explore the Character class from the java.lang package on your own. 10. Redo Exercise 9 without using the Character class. Hint: The ASCII of any uppercase letter will fall between 65 (code for ’A’) and 90 (code for ’Z’). 11. Write a program that reads a sentence and prints out the sentence with all uppercase letters changed to lowercase and all lowercase letters changed to uppercase. 12. Write a program that reads a sentence and prints out the sentence in reverse order. For example, the method will display ?uoy era woH for the input How are you? 13. Write a method that transposes words in a given sentence. For example, given an input sentence The gate to Java nirvana is near the method outputs ehT etag ot avaJ anavrin si raen 536 Chapter 9 Characters and Strings wu23399_ch09.qxd 12/15/06 20:05 Page 536
  • 564. To simplify the problem, you may assume the input sentence contains no punctuation marks. You may also assume that the input sentence starts with a nonblank character and that there is exactly one blank space between the words. 14. Improve the method in Exercise 13 by removing the assumptions. For example, an input sentence could be Hello, how are you? I use JDK 1.2.2. Bye-bye. An input sentence may contain punctuation marks and more than one blank space between two words. Transposing the above will result in olleH, woh era uoy? I esu KDJ 1.2.2. eyB-eyb. Notice the position of punctuation marks does not change and only one blank space is inserted between the transposed words. 15. The Ch9CountWords program that counts the number of words in a given sentence has a bug. If the input sentence has one or more blank spaces at the end, the value for wordCount will be 1 more than the actual number of words in the sentence. Correct this bug in two ways: one with the trim method of the String class and another without using this method. 16. The Ch9ExtractWords program for extracting words in a given sentence includes the test if (beginIdx != endIdx) ... Describe the type of input sentences that will result in the variables beginIdx and endIdx becoming equal. 17. Write an application that reads in a sentence and displays the count of individual vowels in the sentence. Use any output routine of your choice to display the result in this format. Count only the lowercase vowels. Vowel counts for the sentence Mary had a little lamb. # of 'a' : 4 # of 'e' : 1 # of 'i' : 1 # of 'o' : 0 # of 'u' : 0 18. Write an application that determines if an input word is a palindrome. A palindrome is a string that reads the same forward and backward, for example, noon and madam. Ignore the case of the letter. So, for example, maDaM, MadAm, and mAdaM are all palindromes. Exercises 537 wu23399_ch09.qxd 12/15/06 20:05 Page 537
  • 565. 19. Write an application that determines if an input sentence is a palindrome, for example, A man, a plan, a canal, Panama! You ignore the punctuation marks, blanks, and case of the letters. Development Exercises For the following exercises, use the incremental development methodology to implement the program. For each exercise, identify the program tasks, create a design document with class descriptions, and draw the program diagram. Map out the development steps at the start. Present any design alternatives and justify your selection. Be sure to perform adequate testing at the end of each development step. 20. Write an Eggy-Peggy program. Given a string, convert it to a new string by placing egg in front of every vowel. For example, the string I Love Java becomes eggI Leegoveege Jeegaveega 21. Write a variation of the Eggy-Peggy program. Implement the following four variations: • Sha Add sha to the beginning of every word. • Na Add na to the end of every word. • Sha Na Na Add sha to the beginning and na na to the end of every word. • Ava Move the first letter to the end of the word and add ava to it. Allow the user to select one of four possible variations. 22. Write a word guessing game. The game is played by two players, each taking a turn in guessing the secret word entered by the other player. Ask the first player to enter a secret word. After a secret word is entered, display a hint that consists of a row of dashes, one for each letter in the secret word. Then ask the second player to guess a letter in the secret word. If the letter is in the secret word, replace the dashes in the hint with the letter at all positions where this letter occurs in the word. If the letter does not appear in the word, the number of incorrect guesses is incremented by 1. The second player keeps guessing letters until either • The player guesses all the letters in the word. or • The player makes 10 incorrect guesses. Here’s a sample interaction with blue indicating the letter entered by the player: - - - - S - - - - 538 Chapter 9 Characters and Strings wu23399_ch09.qxd 12/15/06 20:05 Page 538
  • 566. A - A - A V - A V A D - A V A J J A V A Bingo! You won. Support the following features: • Accept an input in either lowercase or uppercase. • If the player enters something other than a single letter (a digit, special character, multiple letters, etc.), display an error message. The number of incorrect guesses is not incremented. • If the player enters the same correct letter more than once, reply with the previous hint. • Entering an incorrect letter the second time is counted as another wrong guess. For example, suppose the letter W is not in the secret word. Every time the player enters W as a guess, the number of incorrect guesses is incremented by 1. After a game is over, switch the role of players and continue with another game. When it is the first player’s turn to enter a secret word, give an option to the players to stop playing. Keep the tally and announce the winner at the end of the program. The tally will include for each player the number of wins and the total number of incorrect guesses made for all games. The player with more wins is the winner. In the case where both players have the same number of wins, the one with the lower number of total incorrect guesses is the winner. If the total numbers of incorrect guesses for both players are the same also, then it is a draw. 23. Write another word guessing game similar to the one described in Exercise 22. For this word game, instead of using a row of dashes for a secret word, a hint is provided by displaying the letters in the secret word in random order. For example, if the secret word is COMPUTER, then a possible hint is MPTUREOC. The player has only one chance to enter a guess. The player wins if he guessed the word correctly. Time how long the player took to guess the secret word. After a guess is entered, display whether the guess is correct or not. If correct, display the amount of time in minutes and seconds used by the player. The tally will include for each player the number of wins and the total amount of time taken for guessing the secret words correctly (amount of time used for incorrect guesses is not tallied). The player with more wins is the winner. In the case where both players have the same number of wins, the one who used the lesser amount of time for correct guesses is the winner. If the total time used by both players is the same also, then it is a draw. Exercises 539 wu23399_ch09.qxd 12/15/06 20:05 Page 539
  • 567. 24. The word game Eggy-Peggy is an example of encryption. Encryption has been used since ancient times to communicate messages secretly. One of the many techniques used for encryption is called a Caesar cipher. With this technique, each character in the original message is shifted N positions. For example, if N 1, then the message I d r i n k o n l y d e c a f becomes J ! e s j o l ! p o m z ! e f d b g The encrypted message is decrypted to the original message by shifting back every character N positions. Shifting N positions forward and backward is achieved by converting the character to ASCII and adding or subtracting N. Write an application that reads in the original text and the value for N and displays the encrypted text. Make sure the ASCII value resulting from encryption falls between 32 and 126. For example, if you add 8 (value of N) to 122 (ASCII code for ‘z’), you should “wrap around” and get 35. Write another application that reads the encrypted text and the value for N and displays the original text by using the Caesar cipher technique. Design a suitable user interface. 25. Another encryption technique is called a Vignere cipher. This technique is similar to a Caesar cipher in that a key is applied cyclically to the original message. For this exercise a key is composed of uppercase letters only. Encryption is done by adding the code values of the key’s characters to the code values of the characters in the original message. Code values for the key characters are assigned as follows: 0 for A, 1 for B, 2 for C, . . . , and 25 for Z. Let’s say the key is COFFEE and the original message is I drink only decaf. Encryption works as follows: I d r i n k o n l y d e c a f | | | | | + + + + . . . + | | | | | C O F F E E C O F F E E C O F F E E K – i W . . . j Decryption reverses the process to generate the original message. Write an application that reads in a text and displays the encrypted text. Make sure the ASCII value resulting from encryption or decryption falls between 32 and 126. You can get the code for key characters by (int) keyChar - 65. Write another application that reads the encrypted text and displays the original text, using the Vignere cipher technique. 540 Chapter 9 Characters and Strings wu23399_ch09.qxd 12/15/06 20:05 Page 540
  • 568. 26. A public-key cryptography allows anyone to encode messages while only people with a secret key can decipher them. In 1977, Ronald Rivest, Adi Shamir, and Leonard Adleman developed a form of public-key cryptography called the RSA system. To encode a message using the RSA system, one needs n and e. The value n is a product of any two prime numbers p and q. The value e is any number less than n that cannot be evenly divided into y (that is, y e would have a remainder), where y (p 1) (q 1). The values n and e can be published in a newspaper or posted on the Internet, so anybody can encrypt messages. The original character is encoded to a numerical value c by using the formula c me mod n where m is a numerical representation of the original character (for example, 1 for A, 2 for B, and so forth). Now, to decode a message, one needs d. The value d is a number that satisfies the formula e d mod y 1 where e and y are the values defined in the encoding step. The original character m can be derived from the encrypted character c by using the formula m cd mod n Write a program that encodes and decodes messages using the RSA system. Use large prime numbers for p and q in computing the value for n, because when p and q are small, it is not that difficult to find the value of d. When p and q are very large, however, it becomes practically impossible to determine the value of d. Use the ASCII values as appropriate for the numerical representation of characters. Visit http://www.rsasecurity.com for more information on how the RSA system is applied in the real world. Exercises 541 wu23399_ch09.qxd 12/15/06 20:05 Page 541
  • 570. O b j e c t i v e s After you have read and studied this chapter,you should be able to • Manipulate a collection of data values,using an array. • Declare and use an array of primitive data types in writing a program. • Declare and use an array of objects in writing a program. • Define a method that accepts an array as its parameter and a method that returns an array. • Describe how a two-dimensional array is implemented as an array of arrays. • Manipulate a collection of objects,using lists and maps. 543 10Arrays and Collections wu23399_ch10.qxd 12/28/06 12:38 Page 543
  • 571. eople collect all sorts of items from bottle caps to exotic cars. For proof, just go to eBay (www.ebay.com) and see millions and millions of collectibles up for auction. Now with computers, people amass intangible items such as music files. Exactly how many MP3 files do you have on your computer? Probably in the hundreds and you lost track of exactly how many. You may want to develop a custom software, so you can store the information you want in the format you like, to keep track of all MP3 files downloaded from the Web. When we write a program to deal with a collection of items, say, 500 Student objects, 200 integers, 300 MP3 files, and so forth, simple variables will not work. It is just not practical or feasible to use 500 variables to process 500 Student objects. In theory, you can, but honestly, do you want to type in identifiers for 500 variables (student1, student2, . . .)? A feature supported by programming languages to ma- nipulate a collection of values is an array. In this chapter we will learn about Java arrays. We will learn the basics of array manipulation and how to use different types of arrays properly and effec- tively. In addition, we will study several collection classes from the java.util package that provide more advanced data management features not found in the basic Java arrays. 10.1 Array Basics Suppose we want to compute the annual average rainfall from 12 monthly averages. We can use three variables and compute the annual average as follows (in this and other code fragment examples, we assume scanner is a properly declared and cre- ated Scanner object): double sum, rainfall, annualAverage; sum = 0.0; for (int i = 0; i 12; i++) { System.out.print(Rainfall for month + (i+1) + : ); rainfall = scanner.nextDouble(); sum += rainfall; } annualAverage = sum / 12.0; Now suppose we want to compute the difference between the annual and monthly averages for every month and display a table with three columns, similar to the one shown in Figure 10.1. 544 Chapter 10 Arrays and Collections I n t r o d u c t i o n P wu23399_ch10.qxd 12/28/06 12:38 Page 544
  • 572. To compute the difference between the annual and monthly averages, we need to remember the 12 monthly rainfall averages. Without remembering the 12 monthly averages, we won’t be able to derive the monthly variations after the annual average is computed. Instead of using 12 variables januaryRainfall, februaryRainfall, and so forth to solve this problem, we use an array. An array is a collection of data values of the same type. For example, we may declare an array consisting of double, but not an array consisting of both int and double. The following declares an array of double: double[] rainfall; The square brackets indicate the array declaration. The brackets may be attached to a variable instead of the data type. For example, the declaration double rainfall[]; is equivalent to the previous declaration. In Java, an array is a reference data type. Unlike the primitive data type, the amount of memory allocated to store an array varies, depending on the number and type of values in the array. We use the new operator to allocate the memory to store the values in an array. Although we use the same reserved word new for the array memory allocation as for the creation of a new instance of a class, strictly speaking, an array is not an object. 10.1 Array Basics 545 Annual Average Rainfall: 15.03 mm Month 1 2 3 4 5 6 7 8 9 10 11 12 Average 13.3 14.9 14.7 23.0 25.8 27.7 12.3 10.0 9.8 8.7 8.0 12.2 Variation 1.73 0.13 0.33 7.97 10.77 12.67 2.73 5.03 5.23 6.33 7.03 2.83 Figure 10.1 Monthly rainfall figures and their variation from the annual average. array array declaration In Java,an array is a reference data type.We use the new operator to allocate the memory to store the values in an array. wu23399_ch10.qxd 12/28/06 12:38 Page 545
  • 573. The following statement allocates the memory to store 12 double values and asso- ciates the identifier rainfall to it. rainfall = new double[12]; //create an array of size 12 Figure 10.2 shows this array. We can also declare and allocate memory for an array in one statement, as in double[] rainfall = new double[12]; The number 12 designates the size of the array—the number of values the array con- tains. We use a single identifier to refer to the whole collection and use an indexed expression to refer to the individual values of the collection. An individual value in an array is called an array element. Zero-based indexing is used to indicate the po- sitions of an element in the array. They are numbered 0, 1, 2, . . . , and size – 1, where size is the size of an array. For example, to refer to the third element of the rainfall array, we use the indexed expression rainfall[2] Instead of a literal constant such as 2, we can use an expression such as rainfall[i+3] Notice that the index for the first position in an array is zero. As for a String object, Java uses zero-based indexing for an array. 546 Chapter 10 Arrays and Collections rainfall[2] 0 1 2 3 4 5 6 7 8 9 10 11 rainfall double[] rainfall = new double[12]; This is an indexed expression referring to the element at position 2, that is, the third element of the array. Figure 10.2 An array of 12 double values. indexed expression array element The index of the first position in an array is 0. wu23399_ch10.qxd 12/28/06 12:38 Page 546
  • 574. Using the rainfall array, we can input 12 monthly averages and compute the annual average as double[] rainfall = new double[12]; double annualAverage, sum = 0.0; for (int i = 0; i 12; i++) { System.out.print(Rainfall for month + (i+1) + : ); rainfall[i] = scanner.nextDouble(); sum += rainfall[i]; } annualAverage = sum / 12.0; Figure 10.3 shows how the array will appear after all 12 values are entered. After the 12 monthly averages are stored in the array, we can print out the table (alignment of the columns is not done here, but will be in the complete program listing). double difference; for (int i = 0; i 12; i++) { System.out.print(i+1); //month # //average rainfall for the month System.out.print( + rainfall[i]); //difference between the monthly and annual averages difference = Math.abs( rainfall[i] - annualAverage ); System.out.println( + difference); } Here’s the complete program: 10.1 Array Basics 547 Can also be declared as double rainfall[] = new double[12]; rainfall[2] 28.6 24.5 32.7 18.3 12.5 24.8 9.5 4.5 5.5 12.5 24.5 27.2 0 1 2 3 4 5 6 7 8 9 10 11 28.6 rainfall Figure 10.3 An array of 12 double values after all 12 are assigned values. /* Chapter 10 Sample Program: Compute the annual average rainfall and the variation from monthly average. File: Ch10Rainfall.java */ wu23399_ch10.qxd 12/28/06 12:38 Page 547
  • 575. 548 Chapter 10 Arrays and Collections import java.util.*; class Ch10Rainfall { public static void main (String[] args) { Scanner scanner = new Scanner (System.in); double[] rainfall = new double[12]; double annualAverage, sum, difference; sum = 0.0; for (int i = 0; i 12; i++) { System.out.print(Rainfall for month + (i+1) + : ); rainfall[i] = scanner.nextDouble(); sum += rainfall[i]; } annualAverage = sum / 12.0; System.out.format(Annual Average Rainfall:%5.2fnn, annualAverage); for (int i = 0; i 12; i++) { System.out.format(%3d, i+1); //month # //average rainfall for the month System.out.format(%15.2f, rainfall[i]); //difference between the monthly and annual averages difference = Math.abs( rainfall[i] - annualAverage ); System.out.format(%15.2fn, difference); } } } Notice that the values displayed in the columns are aligned by using the formatting string. An array has a public constant length for the size of an array. Using this con- stant, we can rewrite the for loop as for (int i = 0; i rainfall.length; i++) { ... } length wu23399_ch10.qxd 12/28/06 12:38 Page 548
  • 576. This for loop is more general since we do not have to modify the loop statement when the size of an array is changed. Also, the use of length is necessary when the size of an array is not known in advance.This happens, for example, when we write a method with an array as its parameter. We will provide an example of such a method in Section 10.3. Notice the prompts for getting the values in the previous example are Rainfall for month 1, Rainfall for month 2, and so forth. A better prompt will spell out the month name, for example, Rainfall for January, Rainfall for February, and so forth. We can easily achieve a better prompt by using an array of strings. Here’s how: double[] rainfall = new double[12]; //an array of double String[] monthName = new String[12]; //an array of String double annualAverage, sum = 0.0; monthName[0] = January; monthName[1] = February; monthName[2] = March; monthName[3] = April; monthName[4] = May; monthName[5] = June; monthName[6] = July; monthName[7] = August; monthName[8] = September; monthName[9] = October; monthName[10] = November; monthName[11] = December; for (int i = 0; i rainfall.length; i++) { System.out.print(Rainfall for month + monthName[i] + : ); rainfall[i] = scanner.nextDouble(); sum += rainfall[i]; } annualAverage = sum / 12.0; 10.1 Array Basics 549 It is very easy to mix up the length value of an array and the length method of a String object.The length is a method for a String object,so we use the syntax for calling a method. String str = This is a string; int size = str.length(); But for an array,which is not an object but a reference data type,we do not use the syntax of method calling.We refer to the length value as int size = rainfall.length; wu23399_ch10.qxd 12/28/06 12:38 Page 549
  • 577. Instead of assigning array elements individually, we can initialize the array at the time of declaration. We can, for example, initialize the monthName array by String[] monthName = { January, February, March, April, May, June, July, August, September, October, November, December }; Notice that we do not specify the size of an array if the array elements are initialized at the time of declaration. The size of an array is determined by the number of val- ues in the list. In the above example, there are 12 values in the list, so the size of the array monthName is set to 12. Let’s try some more examples. We assume the rainfall array is declared, and all 12 values are read in. The following code computes the average rainfall for the odd months (January, March, . . .) and the even months (February, April, . . .). double oddMonthSum, oddMonthAverage, evenMonthSum, evenMonthAverage; oddMonthSum = 0.0; evenMonthSum = 0.0; //compute the average for the odd months for (int i = 0; i rainfall.length; i += 2) { oddMonthSum += rainfall[i]; } oddMonthAverage = oddMonthSum / 6.0; //compute the average for the even months for (int i = 1; i rainfall.length; i += 2) { evenMonthSum += rainfall[i]; } evenMonthAverage = evenMonthSum / 6.0; We can compute the same result by using one for loop. for (int i = 0; i rainfall.length; i += 2 ) { oddMonthSum += rainfall[i]; evenMonthSum += rainfall[i+1]; } oddMonthAverage = oddMonthSum / 6.0; evenMonthAverage = evenMonthSum / 6.0; To compute the average for each quarter (quarter 1 has January, February, and March; quarter 2 has April, May, and June; and so forth), we can write for (int i = 0; i 3; i++ ) { quarter1Sum += rainfall[i]; quarter2Sum += rainfall[i+3]; quarter3Sum += rainfall[i+6]; quarter4Sum += rainfall[i+9]; } 550 Chapter 10 Arrays and Collections No size is specified. wu23399_ch10.qxd 12/28/06 12:38 Page 550
  • 578. quarter1Average = quarter1Sum / 3.0; quarter2Average = quarter2Sum / 3.0; quarter3Average = quarter3Sum / 3.0; quarter4Average = quarter4Sum / 3.0; We can use another array to store the quarter averages instead of using four variables: double[] quarterAverage = new double[4]; for (int i = 0; i 4; i++) { sum = 0; for (int j = 0; j 3; j++) { //compute the sum of sum += rainfall[3*i + j]; //one quarter } quarterAverage[i] = sum / 3.0;//average for quarter i+1 } Notice how the inner for loop is used to compute the sum of one quarter. The fol- lowing table illustrates how the values for the variables i and j and the expression 3*i + j change. 10.1 Array Basics 551 i j 3*i + j 0 0 0 1 1 2 2 1 0 3 1 4 2 5 2 0 6 1 7 2 8 3 0 9 1 10 2 11 /* Chapter 10 Sample Program: Compute different statistics from monthly rainfall averages. File: Ch10RainfallStat.java */ Here’s the complete program: wu23399_ch10.qxd 12/28/06 12:38 Page 551
  • 579. 552 Chapter 10 Arrays and Collections import java.util.*; class Ch10RainfallStat { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); String[] monthName = { January, February, March, April, May, June, July, August, September, October, November, December }; double[] rainfall = new double[12]; double[] quarterAverage = new double[4]; double annualAverage, sum, difference; double oddMonthSum, oddMonthAverage, evenMonthSum, evenMonthAverage; sum = 0.0; for (int i = 0; i rainfall.length; i++) { System.out.print(Rainfall for month + monthName[i] + : ); rainfall[i] = scanner.nextDouble(); sum += rainfall[i]; } annualAverage = sum / 12.0; System.out.format( Annual Average Rainfall:%6.2fnn, annualAverage ); oddMonthSum = 0.0; evenMonthSum = 0.0; ///////////// Odd and Even Month Averages ////////////////// //compute the average for the odd months for (int i = 0; i rainfall.length; i += 2) { oddMonthSum += rainfall[i]; } oddMonthAverage = oddMonthSum / 6.0; //compute the average for the even months for (int i = 1; i rainfall.length; i += 2) { evenMonthSum += rainfall[i]; } wu23399_ch10.qxd 12/28/06 12:38 Page 552
  • 580. 10.1 Array Basics 553 evenMonthAverage = evenMonthSum / 6.0; System.out.format(Odd Month Rainfall Average: %6.2fn, oddMonthAverage ); System.out.format(Even Month Rainfall Average:%6.2fnn, evenMonthAverage ); /////////////////// Quarter Averages ////////////////////// for (int i = 0; i 4; i++) { sum = 0; for (int j = 0; j 3; j++) { //compute the sum of sum += rainfall[3*i + j]; //one quarter } quarterAverage[i] = sum / 3.0; //average for quarter i+1 System.out.format(Rainfall Average Qtr.%3d:%6.2fn, i+1, quarterAverage[i] ); } } } In the previous examples, we used a constant to specify the size of an array, such as the literal constant 12 in the following declaration: double[] rainfall = new double[12]; Using constants to declare array sizes does not always lead to efficient space usage. We call the declaration of arrays with constants a fixed-size array declaration. There are two potential problems with fixed-size array declarations. Suppose, for exam- ple, we declare an integer array of size 100: int[] number = new int[100]; The first problem is that the program can process only up to 100 numbers. What if we need to process 101 numbers? We have to modify the program and compile it again. The second problem is a possible underutilization of space. The above de- claration allocates 100 spaces whether they are used or not. Suppose the program on average processes 20 numbers. Then the program’s average space usage is only 20 percent of the allocated space. With Java, we are not limited to fixed-size array declaration. We can declare an array of different size every time we run the fixed-size array declaration wu23399_ch10.qxd 12/28/06 12:38 Page 553
  • 581. program. The following code prompts the user for the size of an array and de- clares an array of designated size: int size; int[] number; System.out.print(Size of an array: ); size = scanner.nextInt(); number = new int[size]; With this approach, every time the program is executed, only the needed amount of space is allocated for the array. Any valid integer arithmetic expression is allowed for size specification, for example, System.out.print(Enter int: ); size = scanner.nextInt(); number = new int[size*size + 2* size + 5]; We call the creation of arrays with nonconstant values a variable-size array creation. This capability comes very handy, for example, when an array runs out of space and we need to create a new, larger array. Suppose we start with an array of 20 elements. What would happen when we had to add the 21st element? We can create a new, larger array, say, twice as large as the original array. We then copy the values from the original array to the new array and finally add the 21st element to the new array. We will show you a concrete example of this scenario in Section 10.7. 554 Chapter 10 Arrays and Collections variable-size array creation Notice the first index position of an array is 0. Java adopted this feature from the programming language C. Using the zero-based indexing, the index value of an element indicates the number of elements in front of the element.For example,an index value of 0 for the first element indicates that there are zero elements in front of it;an index value of 4 for the fifth element indicates that there are four elements in front of it.Zero-based indexing allows a simpler formula to compute the actual memory address of array elements. 1. Which of the following statements are invalid? a. float number[23]; b. float number = { 1.0f, 2.0f, 3.0f }; c. int number; number = new Array[23]; d. int[] number = [ 1, 2, 3, 4 ]; wu23399_ch10.qxd 12/28/06 12:38 Page 554
  • 582. 2. Write a code fragment to compute the sum of all positive real numbers stored in the following array. double[] number = new double[25]; 3. Describe the difference between the following two code fragments. //code fragment 1 for (int i = 0; i number.length; i++) { if ( i % 2 == 0 ) { System.out.println( number[i] ); } } //code fragment 2 for (int i = 0; i number.length; i++) { if ( number[i] % 2 == 0 ) { System.out.println( number[i] ); } } 10.2 Arrays of Objects Array elements are not limited to primitive data types. Indeed, since a String is an object, we actually have seen an example of an array of objects in Section 10.1. In this section we will explore arrays of objects. To illustrate the processing of an array of objects, we will use the Person class in the following examples. We will define this Person class later in the chapter to introduce additional object-oriented concepts. Here’s the portion of the Person class definition we will use in this section: 10.2 Arrays of Objects 555 Public Methods of the Person Class public int getAge ( ) Returns the age of a person.Default age of a person is set to 0. public char getGender( ) Returns the gender of a person.The character F stands for female and M for male. Default gender of a person is set to the character U for unknown. public String getName ( ) Returns the name of a person.Default name of a person is set to Not Given. public void setAge ( int age ) Sets the age of a person. public void setGender( char gender ) Sets the gender of a person to the argument gender.The character F stands for female and M for male.The character U designates unknown gender. public void setName ( String name ) Sets the name of a person to the argument name. wu23399_ch10.qxd 12/28/06 12:38 Page 555
  • 583. The following code creates a Person object: Person latte; latte = new Person( ); latte.setName(Ms. Latte); latte.setAge(20); latte.setGender('F'); System.out.println( Name: + latte.getName() ); System.out.println( Age : + latte.getAge() ); System.out.println( Sex : + latte.getGender() ); Now let’s study how we can create and manipulate an array of Person objects. An array of objects is declared and created just as an array of primitive data types is. The following are a declaration and a creation of an array of Person objects. Person[] person; //declare the person array person = new Person[20]; //and then create it Execution of the above code will result in a state shown in Figure 10.4. Notice that the elements, that is, Person objects, are not yet created; only the array is created. Array elements are initially null. Since each individual element is an object, it also must be created. To create a Person object and set it as the array’s first element, we write person[0] = new Person( ); Figure 10.5 shows the state after the first Person object is added to the array. Notice that no data values are assigned to the object yet. The object has default values at this point. To assign data values to this object, we can execute person[0].setName ( Ms. Latte ); person[0].setAge ( 20 ); person[0].setGender( 'F' ); The indexed expression person[0] 556 Chapter 10 Arrays and Collections Figure 10.4 An array of Person objects after the array is created. 0 1 2 3 4 ••• 16 17 18 19 person - null Person[] person; person = new Person[20]; wu23399_ch10.qxd 12/28/06 12:38 Page 556
  • 584. refers to the first object in the person array. Since this expression refers to an object, we write person[0].setAge( 20 ); to call this Person object’s setAge method, for example. This is the syntax we use to call an object’s method. We are just using an indexed expression to refer to an ob- ject instead of a simple variable. Let’s go through typical array processing to illustrate the basic operations. The first is to create Person objects and set up the person array. We assume that the person array is already declared and created. String name, inpStr; int age; char gender; for (int i = 0; i person.length; i++) { //read in data values System.out.print(Enter name: ); name = scanner.next(); System.out.print(Enter age: ); age = scanner.nextInt(); System.out.print(Enter gender: ); inpStr = scanner.next(); gender = inpStr.charAt(0); //create a new Person and assign values person[i] = new Person( ); person[i].setName ( name ); person[i].setAge ( age ); person[i].setGender( gender ); } 10.2 Arrays of Objects 557 Figure 10.5 The person array with one Person object added to it. 0 1 2 3 4 ••• 16 17 18 19 person :Person Not Given 0 U No values are assigned yet, so this object has default values. wu23399_ch10.qxd 12/28/06 12:38 Page 557
  • 585. Note: To focus on array processing, we used the most simplistic input routine. For instance, we did not perform any input error checking, but this is not to say that input error checking is unimportant. We simply want to focus on array processing here. To find the average age, we execute double sum = 0, averageAge; for (int i = 0; i person.length; i++) { sum += person[i].getAge(); } averageAge = sum / person.length; To print out the name and age of the youngest and the oldest persons, we can execute String nameOfYoungest, nameOfOldest; int min, max, age; nameOfYoungest = nameOfOldest = person[0].getName(); min = max = person[0].getAge(); for (int i = 1; i person.length; i++) { age = person[i].getAge(); if (age min) { //found a younger person min = age; nameOfYoungest = person[i].getName(); }else if (age max) { //found an older person max = age; nameOfOldest = person[i].getName(); } } System.out.println(Oldest : + nameOfOldest + is + max + years old.); System.out.println(Youngest: + nameOfYoungest + is + min + years old.); Instead of using separate String and int variables, we can use the index to the youngest and the oldest persons. Here’s the code: int minIdx, //index to the youngest person maxIdx; //index to the oldest person minIdx = maxIdx = 0; for (int i = 1; i person.length; i++) { if (person[i].getAge() person[minIdx].getAge()) { //found a younger person minIdx = i; 558 Chapter 10 Arrays and Collections find the average age find the youngest and the oldest persons wu23399_ch10.qxd 12/28/06 12:38 Page 558
  • 586. 10.2 Arrays of Objects 559 Figure 10.6 An array of Person objects with two Person variables. 0 1 2 3 4 ••• ••• ••• 16 17 18 19 person :Person Jane 87 F :Person Latte 20 F ••• oldest youngest }else if (person[i].getAge() person[maxIdx.getAge()){ //found an older person maxIdx = i; } } System.out.println(Oldest : + person[maxIdx].getName() + is + person[maxIdx].getAge() + years old.); System.out.println(Youngest: + person[minIdx].getName() + is + person[minIdx].getAge() + years old.); Yet another approach is to use variables for Person objects. Figure 10.6 shows how the Person variables oldest and youngest point to objects in the person array. Here’s the code using Person variables: Person youngest, //points to the youngest person oldest; //points to the oldest person youngest = oldest = person[0]; for (int i = 1; i person.length; i++) { if (person[i].getAge() youngest.getAge()) { //found a younger person youngest = person[i]; } wu23399_ch10.qxd 12/28/06 12:38 Page 559
  • 587. else if (person[i].getAge() oldest.getAge()) { //found an older person oldest = person[i]; } } System.out.println(Oldest : + oldest.getName() + is + oldest.getAge() + years old.); System.out.println(Youngest: + youngest.getName() + is + youngest.getAge() + years old.); Our next example is to search for a particular person. We can scan through the array until the desired person is found. Suppose we want to search for a person whose name is Latte. If we assume the person is in the array, then we can write int i = 0; while (!person[i].getName().equals(Latte)) { i++; } System.out.println(Found Ms. Latte at position + i); The expression person[i].getName().equals(Latte) is evaluated left to right and is equivalent to Person p = person[i]; String str= p.getName(); str.equals(Latte); In this example, we assume that the person for whom we are searching is in the array. If we cannot assume this, then we need to rewrite the terminating condi- tion to take care of the case when the person is not in the array. Here’s how: int i = 0; while (i person.length //still more persons to search !person[i].getName().equals(Latte)) { i++; } if (i == person.length) { //not found - unsuccessful search System.out.println(Ms. Latte was not in the array); } else { //found - successful search System.out.println(Found Ms. Latte at position + i); } 560 Chapter 10 Arrays and Collections find a particu- lar person wu23399_ch10.qxd 12/28/06 12:38 Page 560
  • 588. Here’s the complete program that summarizes the topics covered so far in this section: 10.2 Arrays of Objects 561 /* Chapter 10 Sample Program: Illustrate the processing of an array of Person objects File: Ch10ProcessPersonArray.java */ import java.util.*; class Ch10ProcessPersonArray { public static void main (String[] args) { Person[] person; //declare the person array person = new Person[5]; //and then create it //----------- Create person Array -------------------// String name, inpStr; int age; char gender; for (int i = 0; i person.length; i++) { //read in data values System.out.print(Enter name: ); name = scanner.next(); System.out.print(Enter age: ); age = scanner.nextInt(); System.out.print(Enter gender: ); inpStr = scanner.next(); gender = inpStr.charAt(0); //create a new Person and assign values person[i] = new Person( ); person[i].setName ( name ); person[i].setAge ( age ); person[i].setGender( gender ); } //-------------- Compute Average Age --------------// float sum = 0, averageAge; for (int i = 0; i person.length; i++) { sum += person[i].getAge(); } wu23399_ch10.qxd 12/28/06 12:38 Page 561
  • 589. averageAge = sum / (float) person.length; System.out.println(Average age: + averageAge); System.out.println(n); //------ Find the youngest and oldest persons ----------// //------ Approach No. 3: Using person reference --------// Person youngest, //points to the youngest person oldest; //points to the oldest person youngest = oldest = person[0]; for (int i = 1; i person.length; i++) { if (person[i].getAge() youngest.getAge()) { //found a younger person youngest = person[i]; } else if (person[i].getAge() oldest.getAge()) { //found an older person oldest = person[i]; } } System.out.println(Oldest : + oldest.getName() + is + oldest.getAge() + years old.); System.out.println(Youngest: + youngest.getName() + is + youngest.getAge() + years old.); //----------- Search for a particular person ------------// System.out.print(Name to search: ); String searchName = scanner.next(); int i = 0; while (i person.length //still more persons to search !person[i].getName().equals(searchName)) { i++; } if (i == person.length) { //not found - unsuccessful search System.out.println( searchName + was not in the array ); } else { //found - successful search System.out.println(Found + searchName + at position + i); } } } 562 Chapter 10 Arrays and Collections wu23399_ch10.qxd 12/28/06 12:38 Page 562
  • 590. Now let’s consider the deletion operation. The deletion operation requires some kind of a search routine to locate the Person object to be removed. To concentrate on the deletion operation, we will assume there’s a search method that returns the index of the Person object in the array to be removed.There are two possible ways to remove an object from the array. The first approach is to reset the array element to null. Re- member that each element in an array of objects is a reference to an object, so remov- ing an object from an array could be accomplished by setting the reference to null. Figure 10.7 illustrates how the object at position 1 is deleted by using approach 1. Since any index position can be set to null, there can be “holes,” that is, null references, anywhere in the array. Instead of intermixing real and null references, the second approach will pack the elements so that the real references occur at the beginning and the null references at the end: 10.2 Arrays of Objects 563 Figure 10.7 Approach 1 deletion:setting a reference to null. The array length is 4. 0 1 Before Remove this 2 3 person 0 1 After 2 3 person int delIdx = 1; person[delIdx] = null; A Person object at index 1 is removed. A :Person C :Person D :Person A B C D :Person :Person :Person :Person delete a particular person Real references null references 0 1 2 person 17 i+1 i ••• ••• 18 19 With approach 2, we must fill the hole. There are two possible solutions. The first solution is to pack the elements. If an object at position J is removed (i.e., this po- sition is set to null), then elements from position J+1 up to the last non-null reference are shifted one position lower. And, finally, the last non-null reference is set to null. The second solution is to replace the removed element by the last element in the wu23399_ch10.qxd 12/28/06 12:38 Page 563
  • 591. array. The first solution is necessary if the Person objects are arranged in some order (e.g., in ascending order of age). The second solution is a better one if the Person ob- jects are not arranged in any order. Since we are not arranging them in any order, we will use the second solution. Figure 10.8 illustrates how the object at position 1 is replaced by the last element. The search routine we presented earlier in this section assumes the full array; that is, all elements are non-null references. With the deletion routine, either ap- proach 1 or 2, given above, an array element could be a null. The search routine must therefore be modified to skip the null references (for approach 1) or to stop the search when the first null reference is encountered (for approach 2). In both Figures 10.7 and 10.8, we removed the icon for Person B in the diagrams when the array element was set to null as though the object were erased from the memory. Eventually, the object will indeed be erased, but the operation of assigning null to the array element will not erase the object by itself. The operation simply initiates a chain reaction that will eventually erase the object from the memory. As we have shown several times already, a single object can have multiple ref- erences pointing to it. For example, the following code will result in two references pointing to a single Person object: Person p1, p2; p1 = new Person(); p2 = p1; p1 p2 :Person 564 Chapter 10 Arrays and Collections 0 1 Before 2 3 person 0 1 After 2 3 person int delIdx = 1; int last = 3; person[delIdx] = person[last]; person[last] = null; A Person object at index 1 is removed, and the last object at index 3 replaces the removed object. A B C D :Person :Person :Person :Person A :Person C :Person D :Person Remove this Figure 10.8 Approach 2 deletion:replace the removed element with the last element in the array. The array length is 4. wu23399_ch10.qxd 12/28/06 12:38 Page 564
  • 592. When an object has no references pointing to it, then the system will erase the object and make the memory space available for other uses. We call the erasing of an object deallocation of memory, and the process of deallocating memory is called garbage collection. Unlike in other programming languages, garbage collection is automatically done in Java, so we do not have to be conscious of it when develop- ing Java programs. 10.3 The For-Each Loop 565 garbage collection 1. Which of these statements are invalid? a. Person[25] person; b. Person[ ] person; c. Person person[] = new Person[25]; d. Person person[25] = new Person[25]; 2. Write a code fragment to print out the names of those who are older than 20. Assume the following declaration and that the array is already set up correctly. Person[ ] friend = new Person[100]; 10.3 The For-Each Loop In Chapter 6, we mentioned a new form of the for loop that is introduced in Java 5.0. There is no official name to this for loop, but the term for-each is used most often. The term enhanced for loop is also used by many to refer to this for loop. We will use both terms interchangeably in this book. We will show here how to use the for-each loop in processing an array. We will show how to use it in processing a collection in Section 10.5. Let’s assume number is an int array of 100 integers. Using the standard for loop, we compute the sum of all elements in the number array as follows: int sum = 0; for (int i = 0; i number.length; i++) { sum = sum + number[i]; } We can also compute the sum by using a for-each loop as follows: int sum = 0; for (int value : number) { sum = sum + value; } wu23399_ch10.qxd 12/28/06 12:38 Page 565
  • 593. The loop iterates over every element in the number array, and the loop body is executed for each iteration. The variable value refers to each element in the array during the iteration. So we can interpret this loop as saying something like “For each value in number, execute the following loop body.” The general syntax for the for-each loop is for ( type variable : array ) loop body where type is the data type of variable, array the name of the array, and loop body is a sequence of 0 or more statements (the left and right braces are required if there is more than one statement in the loop body). Let’s look at another example. This time we use an array of objects. Suppose we have an array of 100 Person objects called person: Person[] person = new Person[100]; The Person class is defined in Section 10.2. Assuming that 100 Person objects are created and assigned to person[0] to person[99], we can list the name of every person in the array by using the following for-each loop: for (Person p : person) { System.out.println(p.getName()); } Contrast this to the standard for loop: for (int i = 0; i person.length; i++) { System.out.println(person[i].getName()); } The for-each loop is, in general, cleaner and easier to read. There are several restrictions on using the for-each loop. First, you cannot change an element in the array during the iteration. The following code does not reset the array elements to 0: int [] number = {10, 20, 30, 40, 50}; for (int value : number){ value = 0; } for (int value : number) { System.out.println(value); } 566 Chapter 10 Arrays and Collections This loop has no effect. wu23399_ch10.qxd 12/28/06 12:38 Page 566
  • 594. The first for-each loop has no effect, so the output from this code will be 10 20 30 40 50 We can characterize the for-each loop as a read-only iteration of the elements. 10.3 The For-Each Loop 567 The for-each loop only allows access to the elements. The elements cannot be changed. For an array of objects, an element is actually a reference to an object, so the fol- lowing for-each loop is ineffective. Specifically, it does not reset the elements to null. Person[] person = new Person[100]; for (int i = 0; i person.length; i++) { person[i] = ...; //code to create a new Person object } for (Person p : person) { p = null; } Although we cannot change the elements of an array, we can change the content of an object if the element is a reference to an object. For example, the following for-each loop will reset the names of all objects to Java: Person[] person = new Person[100]; for (int i = 0; i person.length; i++) { person[i] = ...; //code to create a new Person object } for (Person p : person) { p.setName(Java); } Notice that we are not changing the elements (references to objects) themselves, but the content of the objects referenced by these elements. Thus, the code is effective. This loop is effective. The name of every Person object is set to Java. This loop has no effect. wu23399_ch10.qxd 12/28/06 12:38 Page 567
  • 595. The second restriction is that we cannot access more than one array using a single for-each loop. Suppose we have two integer arrays num1 and num2 of length 200 and want to create a third array num3 whose elements are sum of the corre- sponding elements in num1 and num2. Here’s the standard for loop: int[] num1 = new int[200]; int[] num2 = new int[200]; int[] num3 = new int[200]; //code to assign values to the elements of num1 and num2 //compute the sums for (int i = 0; i num3.length; i++) { num3[i] = num1[i] + num2[i]; } Such a loop cannot be written with a for-each loop. 568 Chapter 10 Arrays and Collections The for-each loop allows access to only a single array. The third restriction is that we must access all elements in an array from the first to the last element. We cannot, for example, access only the first half or the last half of the array. We cannot access elements in reverse order either. The for-each loop iterates over every element of an array from the first to the last element. We cannot use the for-each loop to access only a portion of an array or to access the elements in reverse order. This restriction complicates the matter when we try to access elements in an array of objects. Consider the following code: Person[] person = new Person[100]; for (int i = 0; i 50; i++) { person[i] = ...; //code to create a new Person object } wu23399_ch10.qxd 12/28/06 12:38 Page 568
  • 596. 10.4 Passing Arrays to Methods 569 1. Rewrite the following for loop by using a for-each loop. for (int i = 0; i number.length; i++) { System.out.println(number[i]); } 2. Rewrite the following for loop by using the standard for loop. for (Person p : person) { System.out.println(p.getName()); } 3. Why can’t the following for loop be expressed as a for-each loop? for (int i = 0; i number.length; i++) { number[i] = number[i] + 50; } 10.4 Passing Arrays to Methods We discussed the passing of an object to a method by using String objects as illus- trations in Chapter 4. Since both an array and an object are a reference data type, the rules for passing an object to a method and returning an object from the method apply to arrays also. However, there are some additional rules we need to remember in passing an array to a method and returning it from a method. We will cover these topics in this section. Let’s define a method that returns the index of the smallest element in an array of real numbers. The array to search for the smallest element is passed to the method. Here’s the method: public int searchMinimum(double[] number) { int indexOfMinimum = 0; for (int i = 1; i number.length; i++) { if (number[i] number[indexOfMinimum]) { //found a for (Person p : person) { System.out.println( p.getName()); } This code will crash when the variable p is set to the 51st element (i.e., an element at index position 50), because the element is null. Notice that only the first 50 ele- ments actually point to Person objects. The elements in the second half of the array are all null. This loop will result in a NullPointerException error. wu23399_ch10.qxd 12/28/06 12:38 Page 569
  • 597. indexOfMinimum = i; //smaller element } } return indexOfMinimum; } Notice that we use the square brackets to designate that number is an array. The square brackets may also be attached to the parameter, as in public int searchMinimum(double number[]) To call this method (from a method of the same class), we write something like this: double[] arrayOne, arrayTwo; //create and assign values to arrayOne and arrayTwo ... //get the index of the smallest element of arrayOne int minOne = searchMinimum( arrayOne ); //get the index of the smallest element of arrayTwo int minTwo = searchMinimum( arrayTwo ); //output the result System.out.print(Minimum value in Array One is ); System.out.print(arrayOne[minOne] + at position + minOne); System.out.print(nn); System.out.print(Minimum value in Array Two is ); System.out.print(arrayTwo[minTwo] + at position + minTwo); Just like other objects, an array is a reference data type, so we are passing the reference to an array, not the whole array, when we call the searchMinimum method. For example, when the method is called with arrayOne as its argument, the states of memory illustrated in Figures 10.9 and 10.10 will result. There are two references to the same array. The method does not create a separate copy of the array. 570 Chapter 10 Arrays and Collections When an array is passed to a method,only its reference is passed.A copy of the array is not created in the method. Now let’s try another example in which we return an array (actually the refer- ence to the array) from a method. Suppose we want to define a method that inputs wu23399_ch10.qxd 12/28/06 12:38 Page 570
  • 598. 10.4 Passing Arrays to Methods 571 double values and returns the values as an array of double. We can define the method as follows: public double[] readDoubles() { double[] number; System.out.print(How many input values? ); int N = scanner.nextInt(); number = new double[N]; for (int i = 0; i N; i++) { System.out.print(Number + i + : ); number[i] = scanner.nextDouble(); } return number; } arrayOne 1 execution flow Local variables do not exist before the method execution. minOne = searchMinimum(arrayOne); public int searchMinimum(double[] number) { ... } at before calling searchMinimum 1 1 state of memory ••• 2 Memory space for the parameter of searchMinimum is allocated, and the value of arrayOne, which is a reference (address) to an array, is copied to number. So now both variables refer to the same array. minOne = searchMinimum(arrayOne); public int searchMinimum(double[] number) { ... } at after the parameter is assigned 2 2 arrayOne number ••• Figure 10.9 Passing an array to a method means we are passing a reference to an array. We are not passing the whole array. wu23399_ch10.qxd 12/28/06 12:38 Page 571
  • 599. The square brackets beside the method return type double indicate that the method returns an array of double. Because an array is a reference data type, when we say “returns an array of double,” we are really saying “returns the reference to an array of double.” We will use the shorter expression in general and use the longer expression only when we need to be precise. The readDoubles method is called in this manner: double[] arrayOne, arrayTwo; //assign values to arrayOne and arrayTwo arrayOne = readDoubles(); arrayTwo = readDoubles(); 572 Chapter 10 Arrays and Collections arrayOne 4 minOne = searchMinimum(arrayOne); public int searchMinimum(double[] number) { ... } at after searchMinimum 4 4 ••• 3 After the sequence of activities, before returning from the method. Memory space for searchMinimum is deallocated upon exiting the method. The array itself is not part of memory allocated for searchMinimum and will not be deallocated upon exit. minOne = searchMinimum(arrayOne); public int searchMinimum(double[] number) { ... } at before return 3 3 Note: The searchMinimum method did not make any changes to the contents of the array. However, if it did, then the changes made to the array contents will remain in effect because the array is not deallocated. arrayOne number ••• Figure 10.10 Continuation of Figure 10.9. wu23399_ch10.qxd 12/28/06 12:38 Page 572
  • 600. Bad Version Since a new array is created by the method, we do not have to create an array from the calling side. In other words, we don’t have to do this: double[] arrayOne, arrayTwo; arrayOne = new double[30]; //this is NOT necessary arrayOne = readDoubles(); It won’t cause an error if we create an array from the calling side, but we are doing a very wasteful operation. First, it takes up extra memory space. Second, it slows down the whole operation because the computer must garbage-collect the extra memory space that is not being used. Let’s try an alternative approach. This time, instead of creating an array inside the method and returning the array, the calling side creates an array and passes this array to the method: int[] myIntArray = new int[50]; readIntegers(myIntArray); The method readIntegers fills the passed array with integers. The method is defined as follows: public void readIntegers(int[] number) { for (int i = 0; i number.length; i++) { System.out.print(Number + i + : ); number[i] = scanner.nextDouble(); } } Notice the return type of readIntegers is void because we are not returning an array. The method modifies the array that is passed to it. Be careful not to mix the two alternative approaches. The following method will not work: public void badMethod( double[] number ) { System.out.print(How many input values? ); int N = scanner.nextInt(); number = new double[N]; for (int i = 0; i N; i++) { System.out.print(Number + i + : ); number[i] = scanner.nextDouble(); } } 10.4 Passing Arrays to Methods 573 wu23399_ch10.qxd 12/28/06 12:38 Page 573
  • 601. Code such as double[] arrayOne = new double[30]; badMethod( arrayOne ); will leave arrayOne unchanged. Figures 10.11 and 10.12 show the effect of creating a local array in badMethod and not returning it. (Note: The return type of bad- Method is void.) 574 Chapter 10 Arrays and Collections arrayOne 1 Execution flow Local variables do not exist before the method execution. badMethod(arrayOne); badMethod(arrayOne); public void badMethod(double[] number) { ... number = new double[N]; ... } public void badMethod(double[] number) { ... number = new double[N]; ... } at before calling searchMinimum 1 1 state of memory ••• 2 Memory space for the parameter of badMethod is allocated, and the value of arrayOne, which is a reference (address) to an array, is copied to number. So now both variables refer to the same array. at after the parameter is assigned 2 2 arrayOne number ••• Figure 10.11 Effect of creating a local array and not returning it. 1. What will be an output from the following code? int[] list = {10, 20, 30, 40 }; myMethod(list); System.out.println(list[1]); wu23399_ch10.qxd 12/28/06 12:38 Page 574
  • 602. System.out.println(list[3]); ... public void myMethod(int[] intArray) { for (int i = 0; i intArray.length; i+=2) { intArray[i] = i; } } 2. If we replace myMethod of question 1 with the following, what will be an output? public void myMethod(int[] intArray) { int[] local = intArray; for (int i = 0; i local.length; i+=2) { local[i] = i; } } 10.4 Passing Arrays to Methods 575 badMethod(arrayOne); public void badMethod(double[] number) { ... number = new double[N]; ... } 3 A separate array is created and the variable number now refers to this “local” array. at after the local array is created 3 3 arrayOne number ••• ••• This is a newly created array completely separate and different from the argument array. Changes made to this array will not affect the original array. badMethod(arrayOne); public void badMethod(double[] number) { ... number = new double[N]; ... } 4 Memory space for badMethod is deallocated upon exiting the method. The array created by this method now has no variable referring to it, so this array is deallocated also. at after badMethod 4 4 arrayOne ••• Figure 10.12 Continuation of Figure 10.11. wu23399_ch10.qxd 12/28/06 12:38 Page 575
  • 603. 10.5 Two-Dimensional Arrays A table organized in rows and columns is a very effective means for communicating many different types of information. Figure 10.13 shows sample data displayed in a tabular format. In Java, we represent tables as two-dimensional arrays. The arrays we have discussed so far are one-dimensional arrays because they have only one index. In this section, we describe how two-dimensional arrays are used in Java. Let’s begin with an example. Consider the following table with four rows and five columns. The table contains the hourly rate of programmers based on their skill level. The rows (horizontal) represent the grade levels, and the columns (vertical) 576 Chapter 10 Arrays and Collections 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9 2 1 2 4 6 8 10 12 14 16 18 3 3 6 9 12 15 18 21 24 27 4 4 8 12 16 20 24 28 32 36 5 5 Multiplication Table 10 15 20 25 30 35 40 45 6 6 12 18 24 30 36 42 48 54 7 7 14 21 28 35 42 49 56 63 8 8 16 24 32 40 48 56 64 72 9 9 18 27 36 45 54 63 72 81 Los Angeles San Francisco San Jose San Diego Monterey Los Angeles San Francisco San Jose San Diego Monterey — 600 500 150 450 600 — 100 750 150 500 100 — 650 50 150 750 650 — 600 450 150 50 600 — Distance Table (in miles) Tuition Table Grades 1–6 Grades 7–8 Grades 9–12 $16,000.00 $19,000.00 $22,500.00 Day Students $28,000.00 $31,000.00 $34,500.00 Boarding Students Figure 10.13 Examples of information represented as tables. two- dimensional array wu23399_ch10.qxd 12/28/06 12:38 Page 576
  • 604. represent the steps within a grade level. Reading the table, we know a programmer with skill grade level 2, step 1 earns $36.50 per hour. 10.5 Two-Dimensional Arrays 577 Figure 10.14 Accessing an element of a two-dimensional array. 0 0 1 1 2 Row # Column # 2 36.50 3 3 4 payScaleTable[ 2 ][ 1 ] Step 0 1 2 3 4 0 10.50 12.00 14.50 16.75 18.00 1 20.50 22.25 24.00 26.25 28.00 2 34.00 36.50 38.00 40.35 43.00 3 50.00 60.00 70.00 80.00 99.99 Grade We declare the pay scale table as double[][] payScaleTable; or double payScaleTable[][]; and create the array as payScaleTable = new double[4][5]; The payScaleTable array is a two-dimensional array because two indices— one for the row and another for the column—are used to refer to an array element. For example, to refer to the element at the second column (column 1) of the third row (row 2), we say payScaleTable[2][1] Figure 10.14 illustrates how the two indices are used to access an array element of a two-dimensional array. wu23399_ch10.qxd 12/28/06 12:38 Page 577
  • 605. Let’s go over some examples to see how the elements of two-dimensional arrays are manipulated. This code finds the average pay of the grade 2 programmers. double average, sum = 0.0; for (int j = 0; j 5; j++) { sum += payScaleTable[2][j]; } average = sum / 5; The next example prints out the pay difference between the lowest and highest steps for each grade level. double difference; for (int i = 0; i 4; i++) { difference = payScaleTable[i][4] - payScaleTable[i][0]; System.out.println(Pay difference at Grade Level + i + is + difference); } This code adds $1.50 to every skill level. for (int i = 0; i 4; i++) { for (int j = 0; j 5; j++) { payScaleTable[i][j] += 1.50; } } In the previous examples, we used literal constants such as 5 and 4 to keep them simple. For real programs, we need to write a loop that will work for two-dimensional arrays of any size, not just with the one with four rows and five columns. We can use the length field of an array to write such a loop. Using the length field, we can rewrite the third example as for (int i = 0; i payScaleTable.length; i++) { for (int j = 0; j payScaleTable[i].length; j++) { payScaleTable[i][j] += 1.50; } } Do you notice a subtle difference in the code? Let’s examine the difference between the expressions payScaleTable.length and payScaleTable[i].length 578 Chapter 10 Arrays and Collections wu23399_ch10.qxd 12/28/06 12:38 Page 578
  • 606. First, there is actually no explicit structure called two-dimensional array in Java. We only have one-dimensional arrays in Java. However, we can have an array of arrays, and this is how the conceptual two-dimensional array is implemented in Java. The sample array creation payScaleTable = new double[4][5]; is really a shorthand for payScaleTable = new double[4][ ]; payScaleTable[0] = new double[5]; payScaleTable[1] = new double[5]; payScaleTable[2] = new double[5]; payScaleTable[3] = new double[5]; which is equivalent to payScaleTable = new double[4][ ]; for (int i = 0; i 4; i++) { payScaleTable[i] = new double[5]; } Figure 10.15 shows the effect of executing the five statements. The expression payScaleTable.length refers to the length of the payScaleTable array itself. And the expression payScaleTable[1].length refers to the length of an array stored at row 1 of payScaleTable. 0 0 1 2 3 4 1 2 3 payScaleTable payScaleTable[1].length == 5 payScaleTable payScaleTable.length == 4 0 1 2 3 10.5 Two-Dimensional Arrays 579 wu23399_ch10.qxd 12/28/06 12:38 Page 579
  • 607. 580 Chapter 10 Arrays and Collections Figure 10.15 Executing the statements on the left in sequence will create the array of arrays shown on the right. payScaleTable payScaleTable[3] = new double[5]; 0 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 1 2 3 5 payScaleTable payScaleTable[2] = new double[5]; 0 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 1 2 3 4 payScaleTable payScaleTable[1] = new double[5]; 0 0 1 2 3 4 0 1 2 3 4 1 2 3 3 payScaleTable payScaleTable[0] = new double[5]; 0 0 1 2 3 4 1 2 3 2 payScaleTable payScaleTable = new double[4][ ]; 0 1 2 3 1 Executing... Will result in... wu23399_ch10.qxd 12/28/06 12:38 Page 580
  • 608. We call an array that is part of another a subarray. The payScaleTable has four subarrays of the same length. Since we allocate the subarrays individually, we can create subarrays of different lengths. The following code creates a triangular array whose subarray triangularArray[i] has length i. triangularArray = new double[4][ ]; for (int i = 0; i 4; i++) triangularArray[i] = new double[i+1]; The resulting triangularArray looks like this: An array of arrays can be initialized at the time of declaration. The following declaration initializes the payScaleTable array: double[][] payScaleTable = { {10.50, 12.00, 14.50, 16.75, 18.00}, {20.50, 22.25, 24.00, 26.25, 28.00}, {34.00, 36.50, 38.00, 40.35, 43.00}, {50.00, 60.00, 70.00, 80.00, 99.99} }; Here’s the complete sample program: triangularArray 0 0 0 1 0 1 2 0 1 2 3 1 2 3 10.5 Two-Dimensional Arrays 581 /* Chapter 10 Sample Program: Sample program for processing 2-D array of double. File: Ch10PayScaleTable.java */ class Ch10PayScaleTable { public static void main (String[] args) { double[][] payScaleTable = { {10.50, 12.00, 14.50, 16.75, 18.00}, {20.50, 22.25, 24.00, 26.25, 28.00}, {34.00, 36.50, 38.00, 40.35, 43.00}, {50.00, 60.00, 70.00, 80.00, 99.99} }; wu23399_ch10.qxd 12/28/06 12:38 Page 581
  • 609. //Find the average pay of level 2 employees double sum = 0.0, average; for (int j = 0; j 5; j++) { sum += payScaleTable[2][j]; } average = sum / 5; System.out.println( Average of Level 2 Employees: + average ); System.out.println(n); //Display the pay difference at each grade level double difference; for (int i = 0; i 4; i++) { difference = payScaleTable[i][4] - payScaleTable[i][0]; System.out.println(Pay difference at Grade Level + i + is + difference); } //Print out the pay scale table System.out.println(n); for (int i = 0; i payScaleTable.length; i++) { for (int j = 0; j payScaleTable[i].length; j++) { System.out.print( payScaleTable[i][j] + ); } System.out.println(); } //Increase the pay by 1.50 for every level/step //and display the resulting table System.out.println(n); for (int i = 0; i payScaleTable.length; i++) { for (int j = 0; j payScaleTable[i].length; j++) { payScaleTable[i][j] += 1.50; System.out.print(payScaleTable[i][j] + ); } System.out.println(); } } } 582 Chapter 10 Arrays and Collections wu23399_ch10.qxd 12/28/06 12:38 Page 582
  • 610. We can nest for-each loops to process a two-dimensional array. Remember that the two-dimensional array is structurally an array of arrays (as illustrated in Figure 10.15), and the nested for-each loops will make this fact explict. To print out the pay scale table, for example, we write for (double[] row : payScaleTable) { for (double pay : row) { System.out.print(pay + ); } System.out.println(); } TheouterloopiteratesovertherowsinthepayScaleTable two-dimensionalarray.Each row is one-dimensional array of double, so the type is declared as double[]. And the inner loop iterates over the elements in each row. Notice that we cannot rewrite the other loop statements in the Ch10PayScaleTable program by using the for-each loop. There is no limit to the number of dimensions an array can have. We can declare three-dimensional, four-dimensional, and higher-dimensional arrays. How- ever, arrays with a dimension higher than 2 are not frequently used in object- oriented languages. For example, data that were represented as a three-dimensional array in a non-object-oriented language can be represented more naturally as a one- dimensional array of objects with each object containing an array or some other form of data structure (see Exercise 12 on page 615). 10.6 Lists and Maps 583 1. Write a code fragment to compute the average pay of the pays stored in the payScaleTable array. 2. Write a code fragment that finds the largest integer in this two-dimensional array. int[][] table = new int[10][10]; 3. What is an output from this code? int[][] table = new int[10][5]; System.out.println(table.length); System.out.println(table[4].length); 10.6 Lists and Maps Once an array is created, its capacity cannot be changed. For example, if we create an array of 20 elements, then we are limited to store at most 20 elements in using this array. If we need to add elements, then we have to create a new array. (Note: We will learn how to do this in Section 10.7.) We call the condition in which an array does not have any unused position left to add another element an array overflow. array overflow wu23399_ch10.qxd 12/28/06 12:38 Page 583
  • 611. Bad Version Whenever we use arrays in an application, we need to consider the possibility of an array overflow. We can usually avoid an array overflow by declaring its capac- ity to be a large number. However, if we declare the capacity of an array too large, we may avoid an array overflow but end up underutilizing the space (e.g., using only 20 positions in an array with the capacity of 500). If we do not want our application to be limited to some fixed capacity, we need to write code that handles an array overflow by allocating a larger array. If we need to handle an array overflow for multiple arrays we use in an appli- cation, we can define a class that handles the array overflow so we don’t have to im- plement an overflow-handling code for individual arrays. We might call the new class ExpandableArray. By using this class, we can keep adding new elements with- out worrying about the overflow condition, because the class handles the overflow condition automatically. Itturnsoutthere’snoneedforustowritesuchanExpandableArray classbecause the Java standard library java.util already includes various classes and (interfaces) for maintaining a collection of objects. They are collectively referred as the Java Collec- tion Framework, or JCF. We will study the basic ones in this section. The first is the List interface. Like a class, an interface is a reference data type; but unlike a class, an interface includes only constants and abstract methods. An abstract method has only the method header (or, more formally, the method proto- type); that is, it has no method body. The abstract methods of an interface define a behavior. For example, the List interface includes 25 abstract methods that collec- tively define a behavior of a linear list, such as adding an element, removing an element, and so forth. We cannot create an instance of a Java interface. (Note: To differentiate a user interface from a reference data type interface, we will use the term Java interface to refer to the latter.) For example, the following will result in a compile-time error: List myList = new List ( ); To create an instance that will support a List behavior, we need a class that im- plements the List interface. We say a class implements an interface if it provides the method body to all the abstract methods defined in the Java interface. There are two classes in JCF that implement the List interface: ArrayList and LinkedList. Because they implement the same interface, they behave exactly the same. That is, there’s no difference in using them (as long as we use the methods defined in the List interface). They differ in the internal data structure they use to implement the interface. The ArrayList class uses an array, and the LinkedList class uses a technique called linked-node representation. We choose one over the other depending on the nature of application (e.g., choose LinkedList if the application requires frequent insertions and deletions of elements but occasional searching for elements in the list). It is beyond our scope to provide an in-depth comparative analysis here. In most situations, the ArrayList class would be preferable so we will use it for the examples in this chapter. We will provide more detailed coverage and analysis on the array versus linked-node representation in Chapters 16 and 18. 584 Chapter 10 Arrays and Collections JCF interface abstract method java.util wu23399_ch10.qxd 12/28/06 12:38 Page 584
  • 612. Let’s study how we can use the methods of the List interface. First we need to declare and create an instance of a class that implements the List interface. Let’s use the ArrayList class. From what we have learned so far about declaring and creating an instance of a class, we would write something like ArrayList myList; ... Not recommended myList = new ArrayList(); if the class is ArrayList. This would work (you’ll get only a compiler warning), but it is not a recommended style of programming. There are two improvements we should make. The first is to declare the variable as the Java interface and to assign an instance of the class that implements the interface. Applying this improvement will result in List myList; myList is declared as ... type List myList = new ArrayList(); Basically this style of declaration improves the ease of program modification. Suppose, for example, there are many methods that accept a list object public void myMethod(ArrayList aList) With this declaration, we can only pass an instance of ArrayList. Now suppose at a later time we decide to use LinkedList to improve performances for certain types of operations. We have to go back and make changes to all those method headers. But what if we declare the method from the beginning as follows? public void myMethod(List aList) Since we can pass an instance of either ArrayList or LinkedList (or an instance of any class that implements the List interface), no changes are required. We will study the Java interface in greater detail in Chapter 13. The second improvement is specific to Java 5.0 JCF classes. If we declare a list object as List myList = new ArrayList(); there are no restrictions on the type of objects we can add to the list. For example, we can add String objects, Person objects, Vehicle objects, and so forth to this myList. 10.6 Lists and Maps 585 There is another “expandable array” in the JCF called Vector. The Vector class pre- dates the JCF classes,but from Java 2 SDK 1.2,the class was modified to implement the List interface. Because it was designed before the JCF classes, it includes many methods in addition to the List methods. In general, the ArrayList class is recom- mended for most situations. wu23399_ch10.qxd 12/28/06 12:38 Page 585
  • 613. We call such list a heterogeneous list. In most applications, there is no need to maintain such heterogeneous lists. What we need is a homogeneous list, where the elements are restricted to a specific type such as a list of Person objects, a list of String objects, a list of Book objects, and so forth. Specifying the element type im- proves the program reliability because an error such as trying to add a wrong type of object to a list can be caught during the compile time. It is strongly recommended to use homogeneous lists. To specify a homogeneous list, we must include the type of elements in the declaration and creation statements. Here’s an example that declares and creates a list of Person objects: ListPerson friends; ... friends = new ArrayListPerson( ); The general syntax for the declaration is interface-or-class-name element-type identifier; And the general syntax for the creation is identifier = new class-name element-type ( parameters ) ; We can combine the two into a single statement as interface-or-class-name element-type identifier = new class-name element-type ( parameters ) ; for example, ListPerson friends = new ArrayListPerson{ ); Now we are ready to study the basic operations of the List interface. Once a list is created properly, we can start adding elements. In the following example, we cre- ate a list named friends and add four Person objects to the list: ListPerson friends = new ArrayListPerson{ ); Person person; person = new Person(Jane, 10, 'F'); friends.add(person); person = new Person(Jack, 16, 'M'); friends.add(person); person = new Person(Jill, 8, 'F'); friends.add(person); person = new Person(John, 12, 'M'); friends.add(person); 586 Chapter 10 Arrays and Collections add heterogeneous list homogeneous list wu23399_ch10.qxd 12/28/06 12:38 Page 586
  • 614. To find out the number of elements in a list, we use its size method. The following code will print out 3: ListString sample = new ArrayListString{ ); sample.add(One Java); sample.add(One Java); sample.add(One Java); System.out.println(sample.size()); We can access objects in a list by giving their index position in the list, much as we did with the array. We use the get method to access an object at index posi- tion i. For example, to access the Person object at position 3 (note: the first element is at position 0) in the friends list, we write Person p = friends.get(3); An invalid argument, such as a negative value or a value greater than size() – 1, will result in an IndexOutOfBoundsException error. One of the most common operations we perform on a list is traversal. This op- eration, also called scanning or iteration, accesses all elements in a list. To traverse a list from the first to the last element, we can use the for-each loop. Here’s how we can print out the names of all those in the friends list by using the for-each loop: for (Person p : friends) { System.out.println(p.getName()); } This for-each loop, new to Java 5.0, is actually a shortcut for using an iterator pattern. When we call the iterator method of a list, it returns an Iterator object (an instance of a class that implements the Iterator interface) that supports the two methods hasNext and next. Here’s the code to print out the names of all those in the friends list by using an iterator: Person p; IteratorPerson itr = friends.iterator(); while (itr.hasNext()) { p = itr.next(); System.out.println(p.getName()); } Again, the for-each loop is built on top of the iterator pattern as a syntactical shortcut, and wherever the iterator pattern is available, we can (and should) use the cleaner and less error-prone for-each loop. 10.6 Lists and Maps 587 get size traversal iterator wu23399_ch10.qxd 12/28/06 12:38 Page 587
  • 615. The traversal operation is necessary to search a list for elements that meet some criterion. Let’s say we want to print out the names of those in the friends list who are older than 10. Here’s the code: for (Person p : friends) { if (p.age() 10) { System.out.println(p.getName()); } } Instead of simply printing out their names, we could create another list to keep track of those who are older than 10: ListPerson olderThan10List = new ArrayListPerson(); for (Person p : friends) { if (p.age() 10) { olderThan10List.add(p); } } The original friends list remains unchanged; that is, no objects are removed from the list. We simply have a second list that points to a subset of elements in the friends list. The situation can be illustrated as follows (two Person objects are older than 10): To remove an element from a list, we use the remove method. There are two versions: one specifies the index position of the object to remove, and the other spec- ifies the object itself (i.e., the reference to this object). If we use the first version, here’s how we remove the Person object at index position 2 in the friends list: friends.remove(2); The second version of the remove method requires a reference to an object. One way to acquire a reference to an object we want to remove is via traversal. friends olderThan10List :Person :Person :Person :Person :Person :Person ••• ••• 588 Chapter 10 Arrays and Collections remove wu23399_ch10.qxd 12/28/06 12:38 Page 588
  • 616. Bad Version Here’s the code that traverses the friends list and removes all Person objects who are older than 10: ListPerson tempList = new ArrayListPerson(); //first we collect those we want to remove from the //friends list in a separate list for (Person p : friends) { if (p.age() 10) { tempList.add(p); } } //then we remove every element in tempList //from the friends list for (Person p : tempList) { friends.add(p); } Some of you might have thought about the following code: for (Person p : friends) { if (p.age() 10) { friends.remove(p); } } This is an invalid operation. We are not allowed to modify the list we are traversing. The for-each loop (and the underlying iterator pattern) is a read-only traversal. 10.6 Lists and Maps 589 No changes can be made to a list while traversing it with an iterator or a for-each loop. Lists and Primitive Data Types With an array, we can store either primitive data values (int, double, etc.) or ob- jects. With a list, we can store only objects. If we need to store primitive data values wu23399_ch10.qxd 12/28/06 12:38 Page 589
  • 617. in a list, then we must use wrapper classes such as Integer, Float, and Double. To add integers to a list, we have to do something like this: ListInteger intList = new ArrayListInteger( ); intList.add(new Integer(15)); intList.add(new Integer(30)); ... When we access the elements of intList, which are Integer objects, we need to use the intValue method to get the integer value. The following code computes the sum of integer values stored in intList: int sum = 0; for (Integer intObj : intList) { sum = sum + intObj.intValue(); } Instead of this tedious way of dealing with primitive data values, Java 5.0 in- troduces automatic boxing and unboxing features. With Java 5.0, we can write the code as if we could store primitive data values in lists. For example, the following code is valid with Java 5.0: ListInteger intList = new ArrayListInteger( ); intList.add(15); intList.add(30); ... int sum = 0; for (int value : intList) { sum = sum + value; } Keep in mind that there are no structural changes. We are still adding Integer objects to intList (see the declaration for intList). It is just a syntactical shortcut. When we write, for example, intList.add(30); the compiler translates it to intList.add(new Integer(30)); This is called auto boxing. And when we write int num = intList.get(1); 590 Chapter 10 Arrays and Collections wu23399_ch10.qxd 12/28/06 12:38 Page 590
  • 618. the compiler translates it to int num = intList.get(1).intValue( ); This is called auto unboxing. Let’s conclude our discussion of the List interface with the BookTracker helper class we used in the Sample Development section of Chapter 7. The BookTracker class uses an ArrayList to keep track of library books. Here’s the definition: 10.6 Lists and Maps 591 /* Chapter 7 Sample Development: Library Overdue Checker File: BookTracker.java */ import java.util.*; class BookTracker { public static final int ERROR = -1; private ListLibraryBook books; public BookTracker( ) { books = new LinkedListLibraryBook(); } public void add(LibraryBook book) { books.add(book); } public double getCharge( ) { return getCharge(new GregorianCalendar()); //set today as due date } public double getCharge(GregorianCalendar returnDate) { if (books.isEmpty()) { return ERROR; } else { return totalCharge(returnDate); } } public String getList( ) { StringBuffer result = new StringBuffer(); String lineSeparator = System.getProperty(line.separator); for (LibraryBook book: books) { result.append(book.toString() + lineSeparator); } Constructor add getCharge getList wu23399_ch10.qxd 12/28/06 12:38 Page 591
  • 619. return result.toString(); } private double totalCharge(GregorianCalendar returnDate) { double totalCharge = 0.0; for (LibraryBook book: books) { totalCharge += book.computeCharge(returnDate); } return totalCharge; } } 592 Chapter 10 Arrays and Collections totalCharge Map Let’s move on to another useful interface called Map. There are two classes that im- plement this interface: HashMap and TreeMap. We will describe the TreeMap class in this section because this is the class we used in implementing the helper WordList class in Chapter 9. The TreeMap class actually implements a subinterface of Map called SortedMap, where the entries in the map are sorted. A map consists of entries, with each entry divided into two parts: key and value. No duplicate keys are allowed in the map. Both key and value can be an in- stance of any class. The main advantage of a map is its performance in locating an entry, given the key. Consider, for example, that we want to maintain a table of course evaluations. For each course offered on campus, we want to keep an evalua- tion that is summarized from the student opinion poll collected at the end of the term. The course number (e.g., CS0101) is the key, and the evaluation is the value. We would want to store the information as a map because we need to look up the evaluation of a course efficiently, as there are hundreds or thousands of courses. The search would take too long if we used other data structures. As we know, a Java array allows only integer indices. In some situations we may want to use an array with indices other than integers. For example, a WordList from Chapter 9 can be viewed as an array of numbers with words (String) as its indices. A map can be characterized as an expandable array with instances of any class as its indices. So whenever we need an array of values with noninteger indices, a map is a possible solution. When declaring and creating a map, we must specify the type for the key and the value. For example, to declare and create a map with String as both its key and value, we write MapString,String table; table = new TreeMapString,String( ); wu23399_ch10.qxd 12/28/06 12:38 Page 592
  • 620. We use its put method to add the key-value pairs to the map as table.put(CS0101, Great course. Take it); where the first argument is the key and the second argument is the value. To remove an entry, we use the remove method with the key of an entry to remove from the map, for example, table.remove(CS2300); Instead of removing individual elements, we can remove all of them at once by calling the clear method. The statement table.clear( ); removes everything from the map, making it an empty map. To retrieve the value associated to a key, we call the map’s get method. String courseEval = table.get(CS102); We can ask the map if it contains a given key. To check, for example, whether the map contains an evaluation for course number CS0455, we write boolean result = table.containsKey(CS0455); If there’s no matching entry, then the value null is returned. To traverse a map, we must first call its entrySet method to get a set of ele- ments. The method returns an instance of a class that implements the Set interface, another interface in JCF, that models a mathematical set. Those interested in using the Set interface are referred to the Java API documentation. The methods defined in the Set interface are very similar to those defined in the List interface. If we know how to use the List interface, then it won’t take long for us to understand the Set interface. An element in a map is a key-value pair, so the entrySet method returns a set of key-value pairs. A key-value pair is an instance of a class that implements the Map.Entry interface. The dot notation indicates that the Entry interface is defined in the declaration of the Map interface. Such a nested declaration is useful in avoiding a naming conflict. Two useful methods defined in the Map.Entry interface are the getKey and getValue, whose purpose is to retrieve the key and the value of an entry, respec- tively. To put it all together, here’s an example that outputs the course numbers and their evaluations stored in the table map: for (Map.EntryString,String entry : table.entrySet()) { System.out.println(entry.getKey() + :n + entry.getValue() + n); } 10.6 Lists and Maps 593 put remove clear get contains key entrySet wu23399_ch10.qxd 12/28/06 12:38 Page 593
  • 621. Notice the type declaration for the loop variable entry is Map.EntryString, String. Because the key and value component of Map.Entry can be of any class, we need to indicate the actual type for the key and the value specific to this entry. We are now ready to present the WordList class. It uses a TreeMap object to keep track of distinct words in a document and how many times they occur in the document. Notice the TreeMap class actually implements a more specialized map interface called SortedMap, a subinterface of the Map interface that adds the be- havior of sorting the elements in ascending key order. This is exactly the data struc- ture we want to use here because we want to access and display the words and their count in alphabetical order. Here’s the definition: 594 Chapter 10 Arrays and Collections /* Chapter 9 Sample Development: Word Concordance File: WordList.java */ import java.util.*; class WordList { SortedMapString, Integer table; public WordList( ) { table = new TreeMapString, Integer(); } public void add(String word) { int val; if (table.containsKey(word)) { val = table.get(word) + 1; } else { //word occurs for the first time val = 1; } table.put(word, val); } public String getConcordance( ){ String line; String lineTerminator = System.getProperties().getProperty(line.separator); StringBuffer strBuf = new StringBuffer(); for (Map.EntryString,Integer entry : table.entrySet()) { Constructor add getConcordance Auto boxing and unboxing are used in this method. wu23399_ch10.qxd 12/28/06 12:38 Page 594
  • 622. line = entry.getValue().toString() + t + entry.getKey() + lineTerminator; strBuf.append(line); } return strBuf.toString(); } public void reset( ) { table.clear(); } } 10.6 Lists and Maps 595 Compared to the amount of work the class has to perform, the length of its source code is rather short. This is so because the hard part of maintaining the data structure is done by the TreeMap class. Had we tried to implement the WordList class without using the TreeMap class, the source code would have been much longer.Alittle effort to study the JCF classes pays handsomely when the time comes for us to implement an efficient data manager class, such as the WordList class. reset 1. What is the output from the following code? ListString list = new ArrayListString(); for(int i = 0; i 6; i++) { list.add(element + i); System.out.println(list.size()); } 2. What is the output from the following code? ListString list = new ArrayListString(); for(int i = 0; i 6; i++) { list.add(element + i); } list.remove(1); list.remove(3); System.out.println(list.get(2)); 3. Identify all errors in the following code. ListString list = new ArrayListInteger(); ListPerson people = new ListPerson(); MapString table = new Map(); wu23399_ch10.qxd 12/28/06 12:38 Page 595
  • 623. 596 Chapter 10 Arrays and Collections The Address Book In this section, we will design a class called an AddressBook to maintain a collection of Person objects. The AddressBook class is implemented by using an array. We will use the Person class defined in Section 10.2. Through the design of the AddressBook class, we will reiterate the key principles of object-oriented design. Notice that we are not developing a complete program here. We are designing only one of the many classes we need for a complete address book program. For the complete program, we need a main window, objects for doing input and output, and so forth. In this section, we will concentrate on one class that is only responsible for main- taining a collection of Person objects.This class will not perform, for example, input and output of Person objects, following the single-task object (STO) principle introduced in Chapter 4. We will discuss the importance of the STO principle while we develop the AddressBook class. One objective we have in designing the AddressBook class is to make the class reusable in many different programs. Many of the design decisions we will make during the development are based on implementing a reusable class. Problem Statement Write an AddressBook class that manages a collection of Person objects. An AddressBook object will allow the programmer to add, delete, or search for a Person object in the address book. Overall Plan Our first task is to come up with an overall design of the class.Let’s begin by first identify- ing the core operations that an address book object must support. The problem state- ment indicated three major operations: add, delete, and search. These three operations are pretty much a standard in any collection of data values.For any kind of collections,you will always want to be able to add a new item, delete an old item, and search for an item or items.An address book is no exception as it is a collection of information about people for whom you would want to add,delete,and search data. Our task here is to design a class that will maintain an address book by supporting thesethreeoperations.Wewilldefinethreemethodsfortheclass: add,delete,andsearch. Our working design document for the AddressBook class is therefore as follows: Sample Development 10.7 Sample Development Design Document:The Public Methods of the AddressBook Class Method Purpose AddressBook A constructor to initialize the object. We will include multiple constructors as necessary. add Adds a new Person object to the address book. delete Deletes a specified Person object from the address book. search Searches for a specified Person object in the address book and returns this person if found. wu23399_ch10.qxd 12/28/06 12:38 Page 596
  • 624. 10.7 Sample Development 597 We will implement the class in this order: 1. Implement the constructor(s). 2. Implement the add method. 3. Implement the search method. 4. Implement the delete method. 5. Finalize the class. This order of development follows a natural sequence. To implement any instance method of a class, we need to be able to create a properly initialized object, so we will begin the class implementation by defining a constructor. As a part of defining a con- structor,we will identify necessary data members.We will add more data members as we progress through the development steps.The second step is to implement the add rou- tine, because without being able to add a new Person object, we won’t be able to test other operations. For the third step, we will implement the search routine. And for the fourth step,we will implement the last routine.Although we could implement the delete routine before the search routine,we need some form of searching to test the correctness of the delete routine. In other words, we delete a person and attempt to search for this person, verifying that the search will not find the deleted person. So we will implement the search routine before the delete routine. Step 1 Development: Skeleton with Constructors In step 1, we will identify the data members and define the constructor(s) to initialize them.The key data member for the class is a structure we will use to keep track of a col- lection of Person objects.We will use an array for this data structure. Our decision to use an array is based on pedagogy.Using a List from JCF will simplify our development effort, but it is more important to learn how to use arrays. We will create an array of Person objects in the constructor. At the time we create an array, we must declare its size. Remember that the size of an array is the maximum number of elements this array can hold.The actual number of Person objects stored in the array will be anywhere from zero to the size of the array. We have two possible alternatives for specifying the size of an array. First, we can let the programmer pass the size as an argument to the constructor. Second, we can set the size to a default value. Both alternatives are useful. If the programmer has a good estimate of the number of Person objects to manage, she can specify the size in the constructor. Otherwise, she can use the default size by not specifying the size in the constructor. We will define two constructors to support both alternatives. This will give programmers flexibility in creating an AddressBook object. If we are going to provide a constructor in which the programmer can pass the size of an array, then we need to write the constructor so it won’t crash when an invalid value is passed as an argument.What would be an invalid argument value? Since we are dealing with a collection of objects and the size of a collection cannot be negative, an argument value of less than zero is invalid. Also, even though a collection whose size is zero may make sense in theory, such a collection makes no sense in practice.Therefore, develop- ment steps step 1 design wu23399_ch10.qxd 12/28/06 12:38 Page 597
  • 625. 10.7 Sample Development—continued we will consider zero also as an invalid argument value.We will require an argument to a constructor to be a positive integer. We will throw an IllegalArgumentException for an invalid value. At this point, we have only one data member—an array of objects. We will call it entry because a Person object is a single entry in an address book. We will set the default size of entry to 25. There is no particular reason for selecting this size. We simply picked a number that is not too small or too big. We can change this value later if we need to. We will define two constructors. The first constructor will call the second con- structor with the value 25 (default size) as its argument.The second constructor creates an array of Person objects of the size passed as its parameter. Inside the second con- structor, we include a temporary test output statement.The class is defined as follows: 598 Chapter 10 Arrays and Collections step 1 code /** * This class is designed to manage an address book that contains * Person objects. The user can specify the size of the address book * when it is created. If no size is specified, then the default size * is set to 25 Person objects. * * @author Dr. Caffeine * */ class AddressBook { private static final int DEFAULT_SIZE = 25; private Person[] entry; public AddressBook( ) { this( DEFAULT_SIZE ); } public AddressBook(int size) { if (size = 0 ) { //invalid data value, use default throw new IllegalArgumentException(Size must be positive.); } entry = new Person[size]; System.out.println(array of + size + is created.); //TEMP } } Constructors Data members wu23399_ch10.qxd 12/28/06 12:38 Page 598
  • 626. To test this class,we have included a temporary output statement inside the second constructor. We will write a test program to verify that we can create an AddressBook object correctly.The test data are as follows: 10.7 Sample Development 599 step 1 test Step 1 Test Data Data Value Purpose Negative numbers Test the invalid data. 0 Test the end case of invalid data. 1 Test the end case of valid data. 1 Test the normal cases. We will use a very simple test program: /* Chapter 10 Sample Program: A test main program for verifying the step 1 AddressBook class. File: TestAddressBook.java */ import java.util.*; class TestAddressBook { //Step 1 Test Main public static void main(String args[]) { AddressBook myBook; String inputStr; int size; Scanner scanner = new Scanner(System.in); while (true) { System.out.print(Array size: ); inputStr = scanner.next(); if (inputStr.equalsIgnoreCase(stop)) { break; } size = Integer.parseInt(inputStr); wu23399_ch10.qxd 12/28/06 12:38 Page 599
  • 627. 10.7 Sample Development—continued 600 Chapter 10 Arrays and Collections try { myBook = new AddressBook(size); } catch (IllegalArgumentException e) { System.out.println(Exception Thrown: size = + size); } } } } Run the program several times with a different set of test data and verify that we get the correct results. Step 2 Development: Implement the add Method In the second development step, we will implement the add method.We mentioned in the overall design step that this class will not do any input or output of person data. This decision is based on the STO principle. A single object doing both the input/ output routines and maintaining the array will reduce its usability. For example, had the AddressBook class used some GUI objects to handle the input and output of person data, the use of this class would dictate or impose the style of input and output rou- tines on the programmers. The programmer will not have an option of using the input and output objects appropriate for his or her uses. Following the STO principle, we will let the programmer decide how she will input and output person data. The task of the add method is to accept a Person ob- ject as its parameter and add the passed Person object to the array. Since the array is limited in size, what should we do if there is no more space to add another Person object? There are two alternatives. Alternative design 1 is to return false if a new Person object cannot be added to the array, that is, if the array is full. The method will return true otherwise. Alternative design 2 is to increase the array size. Since the size of an array object cannot be changed once the object is created, we need to create another array with a larger size than that of the original if we choose to implement the second alternative. Since the second alternative is more accommodating and less restrictive to the programmer, we will implement this alternative. When the array is full, we will create a new array, copy the objects from the original array to this new array, and finally set the variable entry to point to this new array.We will set the size of the new array to 1.5 times larger than the original array.This size increment is just an estimate. Any value between 125 and 200 percent of the old array is reasonable.You don’t want to make it too small, step 2 design alternative design 1 alternative design 2 wu23399_ch10.qxd 12/28/06 12:38 Page 600
  • 628. say, 105 percent, since that will cause the enlarge method to be called too frequently. You don’t want to make it too large either, since that will likely result in wasted space. Figure 10.16 illustrates the process of creating a larger array. Now let’s think about how to add a Person object to the array. To add a new ob- ject, we need to locate a position where we can insert the object. Since we are not maintaining Person objects in any particular order, we will add a new person at the first available position. If we fill the positions from the low to high indices (0, 1, 2, . . .), we can use a variable to remember the index of the next available position. Since we 10.7 Sample Development 601 A 0 1 2 3 entry B C D :Person :Person :Person :Person 0 1 2 3 4 5 temp Person[] temp; int newLength = (int) (1.5 * entry.length); temp = new Person[newLength]; A 0 1 2 3 entry B C D :Person :Person :Person :Person 0 1 2 3 4 5 temp for (int i = 0; i entry.length; i++) { temp[i] = entry[i]; } entry = temp; Note: The old array will eventually get returned to the system via garbage collection. Figure 10.16 How a new array that is 150 percent of the original array is created. The size of the original array is 4. wu23399_ch10.qxd 12/28/06 12:38 Page 601
  • 629. 10.7 Sample Development—continued 602 Chapter 10 Arrays and Collections are using an array, the index of the next available position is also the number of Person objects currently in the array, so we will call this variable count. Figure 10.17 illustrates the add operation. First we add a new instance variable count to the class: //-------------------------- // Data Members //-------------------------- private int count; //number of elements in the array, //which is also the position at which to add //the next Person object We modify the constructor to initialize this data member: public AddressBook(int size) { count = 0; //same as before } (Note: Because we defined the first constructor to call the second constructor, we can implement this change by rewriting only one constructor instead of two.) The add 0 1 count 2 newPerson 2 3 entry 0 1 count 3 Before After 2 3 entry entry[count] = newPerson; count++; A :Person A :Person B :Person B :Person C :Person newPerson C :Person Figure 10.17 Adding a new Person object to the next available location.The array length is 4. step 2 code wu23399_ch10.qxd 12/28/06 12:38 Page 602
  • 630. 10.7 Sample Development 603 method is defined as follows: public void add(Person newPerson) { assert count =0 count = entry.length; if (count == entry.length) { //no more space left, enlarge( ); //create a new, larger array } //at this point, entry refers to a new, larger array entry[count] = newPerson; count++; } Notice the use of the assertion feature.We place the assert statement to make sure the value of count is valid. The expand method is a new private method that creates a new,larger array. Design Document:The AddressBook Class Method Visibility Purpose . . . . . . . . . expand private Creates a new array that is 150 percent of the old array. private void expand( ) { //create a new array whose size is 150% of //the current array int newLength = (int) (1.5 * entry.length); Person[] temp = new Person[newLength]; //now copy the data to the new array for (int i = 0; i entry.length; i++) { temp[i] = entry[i]; } //finally set the variable entry to point to the new array entry = temp; System.out.println(Inside the method enlarge); //TEMP System.out.println(Size of a new array: + entry.length); //TEMP } Notice the use of the assertion feature here. wu23399_ch10.qxd 12/28/06 12:38 Page 603
  • 631. 10.7 Sample Development—continued We will write a test program to verify that a new Person object is added to the array correctly. In addition, we need to test that a new array 150 percent larger than the old one is created when there are no more spaces left in the array. The test data are as follows: 604 Chapter 10 Arrays and Collections step 2 test The step 2 test program is as follows: /* Chapter 10 Sample Program: A test main program for verifying the step 2 AddressBook class. File: TestAddressBook.java */ class TestAddressBook { public static void main(String[] args) { AddressBook myBook; Person person; myBook = new AddressBook(4); //add four Person objects for (int i = 0; i 4; i++) { person = new Person(Ms. X + i, 10, 'F'); myBook.add(person); } //add the fifth person and see if //a new array is created Step 2 Test Data Test Sequence Purpose Create the array of size 4. Test that the array is created correctly. Add four Person objects. Test that the Person objects are added correctly. Add the fifth Person object. Test that the new array is created and the Person object is added correctly (to the new array). wu23399_ch10.qxd 12/28/06 12:38 Page 604
  • 632. person = new Person(fifth one, 10, 'F'); myBook.add(person); } } 10.7 Sample Development 605 step 3 design Run the program several times with different sizes for the address book and verify that we get the correct results. Step 3 Development: Implement the search Method In the third development step, we implement the search method.This method can return one or more Person objects that meet the search criteria.We have several options for the search criteria.Since we keep track of name,age,and gender for each person,we can use any one of these values as the search criterion. In this implementation, we will use the person’s name.The search routine for the other two criteria will be left as an exercise (see Exercise 14). To implement the search method, we will make an assumption that the name is unique so that there will be at most one matching Person object. If the name is not unique, then there are two possibilities.The search method can return one Person ob- ject (among many) that matches the given name or return all Person objects that match the given name.We will leave the case when the name is not unique as an exer- cise (see Exercise 13). Notice that the add method we implemented in step 2 does not check the person data. In other words, there is no mechanism to disallow the addition of a Person object with a duplicate name. We will leave the implementation of the modified add method as an exercise (see Exercise 15). There are two possible outcomes with the search method—a successful or an un- successful search.The method has to return a value by which the programmer can verify the result of the search.We will define the search method so that it will return a matching Person object if it is found and will return null otherwise. The search routine will start scanning the array from the first position until the desired Person object is found (suc- cessful search) or until no more Person objects are left in the array (unsuccessful search). Expressing the search routine in pseudocode,we have loc = 0; while (loc count name of Person at entry[loc] != searchName) { loc++; } if (loc == count) { foundPerson = null; } else { foundPerson = entry[loc]; } return foundPerson; wu23399_ch10.qxd 12/28/06 12:38 Page 605
  • 633. 10.7 Sample Development—continued Translating the pseudocode to an actual method will result in the following method: public Person search(String searchName) { Person foundPerson; int loc = 0; assert count = 0 count = entry.length; while (loc count !searchName.equals(entry[loc].getName())) { loc++; } if (loc == count) { foundPerson = null; } else { foundPerson = entry[loc]; } return foundPerson; } To test the search method, we will build an address book that contains five Person objects. We will give names Ms.X0, Ms.X1, . . . , and Ms.X4 to them. After the address book is set up, we test various cases of the search. We test for successful and unsuccessful searches. For the successful searches, we test for the end cases and nor- mal cases. The end cases involve searching for persons stored in the first and last posi- tions of the array. Off-by-1 error (OBOE) is very common in processing an array, so it is very important to test these end cases. After a successful execution, we will test the class again by changing the size of the array.One test size we should not forget to test is the end case for the array size,which is 1.Also,we need to test the cases where the array is not fully filled,such as an array of size 5 containing only two Person objects. The test data are as follows: 606 Chapter 10 Arrays and Collections step 3 code step 3 test Step 3 Test Data Test Sequence Purpose Create the array of size 5 and add five Person objects with unique names. Test that the array is created and set up correctly.Here,we will test the case where the array is 100 percent filled. Search for the person in the first position of the array. Test that the successful search works correctly for the end case. wu23399_ch10.qxd 12/28/06 12:38 Page 606
  • 634. The step 3 test program is written as follows: 10.7 Sample Development 607 Search for the person in the last position of the array. Search for a person somewhere in the middle of the array. Test the normal case. Test another version of the end case. Search for a person not in the array. Test for the unsuccessful search. Repeat the above steps with an array of varying sizes,especially the array of size 1. Test that the routine works correctly for arrays of different sizes. Repeat the testing with the cases where the array is not fully filled,say,array length is 5 and the number of objects in the array is 0 or 3. Test that the routine works correctly for other cases. /* Chapter 10 Sample Program: A test main program for verifying the step 3 AddressBook class. File: TestAddressBook.java */ class TestAddressBook { AddressBook myBook; Person person; public static void main(String[] args) { TestAddressBook tester = new TestAddressBook(); tester.setupArray(5); tester.testSearch(); } public void setupArray(int N) { myBook = new AddressBook( N ); //add N Person objects for (int i = 0; i N; i++) { person = new Person(Ms. X+i, 10, 'F'); myBook.add(person); } } public void testSearch( ) { //test for the end case person = myBook.search(Ms. X2); wu23399_ch10.qxd 12/28/06 12:38 Page 607
  • 635. 10.7 Sample Development—continued if (person == null) { System.out.println (Error: Didn't find the person it should); } else { System.out.println (person.getName() + is found okay.); } } } 608 Chapter 10 Arrays and Collections Notice the TestAddressBook class is now an instantiable main class.Since the code for testing is getting longer, it is not practical anymore to do everything in a single main method. For testing, we will modify the method body of setupArray and testSearch as necessary to test all other cases described in the test data table. Step 4 Development: Implement the delete Method In the fourth development step, we implement the delete method. To delete a Person object,the programmer must somehow specify which Person object to remove from the address book. As we did with the search method, we will use the name of a person to specify which person to delete.Since we assume the name is unique,the delete method will remove at most one Person object. There are two possible outcomes: the specified person is removed from the address book (successful operation) and the specified person is not removed because he or she is not in the address book (unsuccessful operation).We will define the delete method so that it will return true if the operation is successful and false otherwise. The removal of an element in an array of objects is done by setting the element to null. This will leave a“hole.”We will fill this hole by replacing the removed element with the last element, as explained earlier (see Figure 10.8).This filling operation is necessary for other methods,specifically the add method,to work correctly. To fill the hole, we need to know the location of the hole.To find this location, we write a private search method called findIndex. The method is very similar to the search method.The only difference is that the return value of findIndex is an index of an element in the array,whereas the return value of search is a Person object.By using this findIndex method,the delete method can be expressed as boolean status; int loc; loc = findIndex( searchName ); step 4 design wu23399_ch10.qxd 12/28/06 12:39 Page 608
  • 636. if (loc is not valid) { status = false; } else { //found, pack the hole replace the element at index loc+1 by the last element at index count; status = true; count--; //decrement count, //since we now have one less element assert count is valid; } return status; The private findIndex method will look like this: private int findIndex(String searchName) { int loc = 0; assert count =0 count = entry.length; while (loc count !searchName.equals(entry[loc].getName())) { loc++; } if (loc == count) { loc = NOT_FOUND; } return loc; } The constant NOT_FOUND is set in the data member section as //--------------------------- // Data Members //--------------------------- private static final int NOT_FOUND = -1; By using this findIndex method,the delete method is defined as follows: public boolean delete(String searchName) { boolean status; int loc; loc = findIndex(searchName); if (loc == NOT_FOUND) { status = false; } else { //found, pack the hole 10.7 Sample Development 609 step 4 code wu23399_ch10.qxd 12/28/06 12:39 Page 609
  • 637. 10.7 Sample Development—continued entry[loc] = entry[count-1]; status = true; count--; //decrement count, //since we now have one less element assert count = 0 count = entry.length; } return status; } To test the delete method,we will build an address book that contains five Person objects, as before.Test cases are to delete the first person in the array, delete the last per- son in the array,delete someone in the middle (normal case),and try to delete a nonexis- tent person. After a successful execution, we will test the class again by changing the size of an array.One test size we should not forget to test is the end case for the array size, which is 1.Also,we need to test the cases where the array is not fully filled,such as an array of size 5 containing only two Person objects. The test data are as follows: 610 Chapter 10 Arrays and Collections step 4 test Step 4 Test Data Test Sequence Purpose Create the array of size 5 and add five Person objects with unique names. Search for a person to be deleted next. Verify that the person is in the array before deletion. Delete the person in the array. Test that the delete method works correctly. Search for the deleted person. Test that the delete method works correctly by checking that the value null is returned by the search. Attempt to delete a nonexistent person. Test that the unsuccessful operation works correctly. Repeat the above steps by deleting persons at the first and last positions. Test that the routine works correctly for arrays of different sizes. Repeat testing where the array is not fully filled,say,an array length is 5 and the number of objects in the array is 0 or 3. Test that the routine works correctly for other cases. Test that the array is created and set up correctly.Here,we will test the case where the array is 100 percent filled. wu23399_ch10.qxd 12/28/06 12:39 Page 610
  • 638. The step 4 test program is written as follows: 10.7 Sample Development 611 /* Chapter 10 Sample Program: A test main program for verifying the step 4 AddressBook class. File: TestAddressBook.java */ class TestAddressBook { AddressBook myBook; Person person; public static void main(String[] args) { TestAddressBook tester = new TestAddressBook(); tester.setupArray( 5 ); tester.testDelete( ); } public void setupArray(int N) { myBook = new AddressBook( N ); //add N Person objects for (int i = 0; i N; i++) { person = new Person( Ms. X + i, 10, 'F' ); myBook.add(person); } } public void testDelete( ) { //first make sure the person is in the array person = myBook.search( Ms. X2 ); if (person == null) { System.out.println(Error: Didn't find the person it should); } else { System.out.println(person.getName() + is found okay.); boolean success = myBook.delete(Ms. X2); if (success) { person = myBook.search(Ms. X2); if (person == null) { System.out.println(Okay: Deletion works); } else { wu23399_ch10.qxd 12/28/06 12:39 Page 611
  • 639. 10.7 Sample Development—continued System.out.println(Error: Person is still there); } } else { System.out.println(Error: Deletion has a problem); } } } } 612 Chapter 10 Arrays and Collections Modify the method body of setupArray and testDelete as necessary to test all other cases described in the step 4 test data table. Step 5 Development: Finalize As always, we finalize the program in the last step.We perform a critical program review to find any inconsistency or error in the methods, incomplete methods, places to add comments,and so forth. Since the three operations of add, delete, and search are interrelated, it is critical to test these operations together. The test program should try out various combinations of add,delete, and search operations to verify that they work together correctly. After we complete the class implementation and testing, we may consider improvement or extension. In addition to the several alternative designs, it is possible to add other operations. For example, we may want to add an operation to modify a Person object. Another common operation that is useful in manipulating a collection of objects is the traversal operation described in Section 10.6. Implementation of this operation is left as Exercise 16. prograrn review final test • An array is an indexed collection of data values. • Data values in an array are called array elements. • Individual elements in an array are accessed by the indexed expression. • Array elements can be values of primitive data type or objects. • In Java, an array can include only elements of the same data type. • A Java array is a reference data type. • A Java array is created with the new operator. • An array can have multiple indices. S u m m a r y wu23399_ch10.qxd 12/28/06 12:39 Page 612
  • 640. • When an array is passed to a method as an argument, only a reference to an array is passed. A copy of an array is not created. Note: The reference to an array we are passing is a value of an array variable, and therefore the call- by-value scheme is used here also. • The standard classes and interfaces described or used in this chapter are List ArrayList LinkedList Exercises 613 Iterator Map HashMap TreeMap • The Java Collection Framework includes many data structure classes such as lists and maps. • The List interface represents a linear ordered collection of objects. • The ArrayList and LinkedList classes are two implementations of the List interface. • The Map interface represents a collection of key-value pairs. • The TreeMap and HashMap classes are two implementations of the Map interface. K e y C o n c e p t s arrays arrays of objects array elements multidimensional arrays index expression lists arrays of primitive data type maps E x e r c i s e s 1. Identify problems with this code: public int searchAccount( int[25] number ) { number = new int[15]; for (int i = 0; i number.length; i++) { number[i] = number[i-1] + number[i+1]; } return number; } 2. Declare an array of double of size 365 to store daily temperatures for one year. Using this data structure, write the code to find • The hottest and coldest days of the year. • The average temperature of each month. wu23399_ch10.qxd 12/28/06 12:39 Page 613
  • 641. • The difference between the hottest and coldest days of every month. • The temperature of any given day. The day is specified by two input values: month (1, . . . , 12) and day (1, . . . , 31). Reject invalid input values (e.g., 13 for month and 32 for day). 3. Repeat Exercise 2, using a two-dimensional array of double with 12 rows and each row having 28, 30, or 31 columns. 4. Repeat Exercise 2, using an array of Month objects with each Month object having an array of double of size 28, 30, or 31. 5. For Exercises 2 to 4, the following three data structures are used: • One-dimensional array of double of size 365. • Two-dimensional array of double with 12 rows. Each row has 28, 30, or 31 columns. • An array of Month objects with each Month object having an array of double of size 28, 30, or 31. Discuss the pros and cons of each approach. 6. Suppose you want to maintain the highest and lowest temperatures for every day of the year. What kind of data structure would you use? Describe the alternatives and list their pros and cons. 7. If a car dealer’s service department wants a program to keep track of customer appointments, which data structure should they choose, an array or a list? If the number of appointments the service department accepts is fixed on any given day, which data structure is appropriate? What are the criteria you use to decide which data structure to use? Explain. 8. In Figure 10.8, the last statement person[last] = null; is significant. Show the state-of-memory diagram when the last statement is not executed. 9. Write an application that computes the standard deviation of N real numbers. The standard deviation s is computed according to s The variable x is the average of N input values x1 through xN. The program first prompts the user for N and then declares an array of size N. 10. Using the payScaleTable two-dimensional array from Section 10.4, write the code to find • The average pay for every grade level • The average pay for every step (i.e., average of every column) 11. Declare a two-dimensional array for the tuition table shown in Figure 10.13. (x1 x )2 (x2 x )2 . . . (xN x )2 N 614 Chapter 10 Arrays and Collections wu23399_ch10.qxd 12/28/06 12:39 Page 614
  • 642. 12. Suppose you want to maintain information on the location where a product is stored in a warehouse. Would you use a three-dimensional array such as location[i][j][k], where i is the warehouse number, j is the aisle number, and k is the bin number? Or would you define three classes Warehouse, Aisle, and Bin? Describe the alternatives and list their pros and cons. 13. The search method of the AddressBook class returns only one Person object. Modify the method so that it will return all Person objects that match the search criteria. You can use an array to return multiple Person objects. 14. Write new search routines for the AddressBook class. The search method given in the chapter finds a person with a given name. Add second and third search methods that find all persons, given an age and a gender, respectively. 15. Modify the add method of the AddressBook class. The method given in the chapter does not check for any duplicate names. Modify the method so that no Person object with a duplicate name is added to the address book. 16. Modify the AddressBook class to allow the programmer to access all Person objects in the address book. Make this modification by adding two methods: getFirstPerson and getNextPerson. The getFirstPerson method returns the first Person object in the book. The getNextPerson method returns the next Person object if there is one. If there is no next person in the book, getNextPerson returns null. The getFirstPerson method must be called before the getNextPerson method is called. 17. In addition to the List and Map interface, the third interface in the Java Collection Framework is Set. A Set is an unordered collection of objects with no duplicates. This interface models, as expected, the mathematical set. Two classes that implement the Set interface in JCF are TreeSet and HashSet. Here’s a simple example of using Set: SetString = new HashSet String(); set.add(ape); set.add(bee); set.add(ape); //duplicate, so it won't be added set.add(cat); set.remove(bee); set.remove(dog); //not in the set, nothing happens System.out.println(Set = + set); The output from the code will be Set = [ape, cat] To access the individual elements of a set, call the iterator method in the manner identical to the one we used for the List interface. Rewrite the AddressBook class by using the HashSet instead of an array to maintain a collection of Person object. Exercises 615 wu23399_ch10.qxd 12/28/06 12:39 Page 615
  • 643. 18. Consider the following Thesaurus class: class Thesaurus { //Returns all synonyms of the word as a Set //Returns null if there is no such word public java.util.SetString get (String word){...} //Returns all key words in this thesaurus as a Set //returns an empty set if there are no keys (if you //don't do anything, default behavior of the //underlying JCF class will handle it) public java.util.SetString keys( ){...} //Adds 'synonym' to the synonym set of 'word' //Pay close attention to this method. public void put (String word, String synonym){...} } The get method returns a set of all synonyms of a given word. The keys method returns all key words in the thesaurus. The put method adds a new synonym to the given word. Make sure to handle the cases when the word already has a synonym list and when the word is added for the first time. Using this Thesaurus class, we can write, for example, this program: class SampleMain { public static void main(String[] args) { Thesaurus t = new Thesaurus(); t.put(fast, speedy); t.put(fast, swift); t.put(slow, sluggish); SetString synonyms = t.get(fast); System.out.println(synonyms); System.out.println(t.keys()); } } When the sample program is executed, the output will be Implement the Thesaurus class, using one of the Map classes. The key is the word, and the value is the set of synonyms of this word. 616 Chapter 10 Arrays and Collections wu23399_ch10.qxd 12/28/06 12:39 Page 616
  • 644. Development Exercises For the following exercises, use the incremental development methodology to implement the program. For each exercise, identify the program tasks, create a design document with class descriptions, and draw the program diagram. Map out the development steps at the start. Present any design alternatives and justify your selection. Be sure to perform adequate testing at the end of each development step. 19. Write a complete address book maintenance application. The user of the program has four options: Add a new person, delete a person, modify the data of a person, and search for a person by giving the name. Use the AddressBook class, either the original one from the chapter or the modified one from the previous exercises. You have to decide how to allow the user to enter the values for a new person, display person information, and so forth. 20. Design a currency converter class whose instance will handle conversion of all currencies. A single instance of the new currency converter class you design here will handle all currencies. Instead of having specific conversion methods such as toDollar, toYen, and so forth, the new currency converter class supports one generic conversion method called exchange. The method has three arguments: fromCurrency, toCurrency, and amount. The first two arguments are String and give the names of currencies. The third argument is float. To convert $250 to yen, we write yen = converter.exchange( dollar, yen, 250.0 ); To set the exchange rate for a currency, we use the setRate method. This method takes two arguments: The first argument is the currency name, and the second argument is the rate. For example, if the exchange rate for yen is 140 yen to $1, then we write converter.setRate( yen, 140.0 ); Use an array to keep track of exchange rates. 21. Extend the MyJava Lo-Fat Burgers drive-through ordering system of Exercise 24 on page 294 so the program can output sales figures. For each item on the menu, the program keeps track of the sales. At closing time, the program will output the sales figure in a format similar to the following: Item Sales Count Total Tofu Burger 25 $ 87.25 Cajun Chicken 30 $ 137.70 ... Today's Total Sales: $ 2761.20 Exercises 617 wu23399_ch10.qxd 12/28/06 12:39 Page 617
  • 645. 22. Redo the watermelon projectile computing program of Exercise 30 on page 362 to output the average distance covered between each time interval. Use the expression (x2 x 1)2 ( y2 y 1)2 to compute the distance between two coordinate points (x1, y1) and (x2, y2). 23. Redo the social club program of Exercise 9 of Chapter 8. In the original program, we limit the number of clubs to 5. Remove this restriction by using an array. 24. Redo Exercise 23, but this time use one of the Java Collection Framework classes. 618 Chapter 10 Arrays and Collections wu23399_ch10.qxd 12/28/06 12:39 Page 618
  • 646. Sorting and Searching O b j e c t i v e s After you have read and studied this chapter,you should be able to • Perform linear and binary search algorithms on arrays. • Determine whether a linear or binary search is more effective for a given situation. • Perform selection and bubble sort algorithms. • Describe the heapsort algorithm and show how its performance is superior to that of the other two algorithms. • Apply basic sorting algorithms to sort an array of objects,using the Comparator interface. • Define the interface to specify common behavior and provide different versions of classes that implement the interface. 619 11 wu23399_ch11.qxd 12/28/06 12:41 Page 619
  • 647. n this chapter, we cover searching and sorting. In Chapter 10, we presented a case study of maintaining an address book and described a basic searching method to locate a student, given his or her name. In this chapter, we will present a better search- ing algorithm called binary search. To apply binary search, an array must be sorted. Sorting is a technique to arrange elements in some order, and it is one of the funda- mental operations we study in computer science. We will cover basic sorting algo- rithms in this chapter and an efficient recursive sorting algorithm in Chapter 15. We will use an array of integers to illustrate searching and sorting algorithms, but all the techniques we present here are equally applicable to any array of objects as well as primitive data types. In the sample development (Section 11.4), we will extend the AddressBook class by adding the capability of sorting an array of Person objects. 11.1 Searching Let’s start with the problem statement for searching: Given a value X, return the index of X in the array, if such X exists. Otherwise, return NOT_FOUND (1). We assume there are no duplicate entries in the array. There are two possible outcomes in searching: Either we locate an X or we don’t. We will call the first a successful search and the latter an unsuccessful search. Figure 11.1 illustrates the successful and unsuccessful searches. As obvious as this may sound, it is critical to differentiate the two because it is possible for one search- ing algorithm to perform superbly for successful searches, but very poorly for unsuccessful searches. When we analyze the performance of a searching algorithm, we normally derive two separate performances, one for a successful search and another for an unsuccessful search. Linear Search The search technique we used earlier in the book is called a linear search because we search the array from the first position to the last position in a linear progression. 620 Chapter 11 Sorting and Searching I n t r o d u c t i o n I successful and unsuccessful searches linear search 0 1 2 3 4 5 6 7 8 number 23 17 5 90 12 44 38 84 77 Unsuccessful search: Successful search: search(45) NOT_FOUND (1) search(12) 4 Figure 11.1 Successful and unsuccessful searches. wu23399_ch11.qxd 12/28/06 12:41 Page 620
  • 648. The linear search is also called a sequential search. The linear search algorithm can be expressed as public int linearSearch (int[] number, int searchValue) { int loc = 0; while ( loc number.length number[loc] != searchValue ) { loc++; } if ( loc == number.length) { //Not found return NOT_FOUND; } else { return loc; //Found, return the position } } If the number of entries in the array is N, then there will be N comparisons for an unsuccessful search (i.e., you search for a value not in the array). In the case of a successful search, there will be a minimum of one comparison and a maximum of N comparisons. On average, there will be approximately N2 comparisons. Is there a way to improve the linear search? If the array is sorted, then we can improve the search routine by using the binary search technique. Binary Search If the values in the array are arranged in ascending or descending order, then we call the array sorted. In the following explanation of the binary search, we assume the array is sorted in ascending order. The crux of binary search is the winning strategy you apply for the Hi-Lo game. When you try to guess a secret number, say, between 1 and 100, your first guess will be 50. If your guess is HI, then you know the secret number is between 1 and 49. If your guess is LO, then you know the secret number is between 51 and 100. By guessing once, you eliminated one-half of the possible range of values from further consideration. This is the core idea of binary search. Consider the following sorted array: Let’s suppose we are searching for 77. We first search the middle position of the array. Since this array has 9 elements, the index of the middle position is 4, so we search number[4]. The value 77 is not in this position. Since 77 is larger than 38 and 0 1 2 3 4 5 6 7 8 number 5 12 17 23 38 44 77 84 90 11.1 Searching 621 More elements to search searchValue is not yet found. binary search wu23399_ch11.qxd 12/28/06 12:41 Page 621
  • 649. the array is sorted, we know that if 77 is in the array, it must be in the right half of the array. So next we search the middle position of the right half of the array, which is position 6. Figure 11.2 illustrates the effect of making one comparison in the binary search. The search value 77 was found after two comparisons. In contrast, the linear search would take seven comparisons to locate 77. So there is a net reduction of five comparisons. How good is the binary search in general? Let’s study the worst-case situation. In the binary search, after we make one comparison, we can eliminate one-half of the array from further consideration. So the number of comparisons in the worst case is the number of times you can divide the array into halves. Suppose the original size of an array is N and the value we are searching for is not in the array. Then after one comparison, we have to search the remaining N2 elements. After two comparisons, we have to search N4 elements, and so on. The following table shows this relationship. The left column is the number of comparisons, and the right column is the number of elements we still need to search after making K comparisons. Number of Comparisons Number of Elements 0 N 1 N2 N21 2 N4 = N22 . . . . . . K N2K 622 Chapter 11 Sorting and Searching 0 1 2 3 4 5 6 7 8 5 12 17 23 38 44 77 84 90 0 1 2 3 4 5 6 7 8 44 77 84 90 Search Search No need to consider the left half anymore. 77 must be in this half of the array. number[i] 38 38 number[i] Figure 11.2 Effect of one comparison in binary search. wu23399_ch11.qxd 12/28/06 12:41 Page 622
  • 650. The maximum number of comparisons K is derived by solving the equation N 2K log2 N K This is a remarkable improvement. If the size of the original array is 2048, for exam- ple, then the unsuccessful binary search takes at most log2 2048 11 comparisons, while the unsuccessful linear search takes 2048 comparisons. The difference be- tween the two algorithms gets larger and larger as the size of an array increases. Now let’s write a binary search method. The key point in the method revolves on how to stop the search. If the search value is in the array, we will eventually locate it, so the stopping condition for the successful search is easy. What about the case for an unsuccessful search? How can we detect that there are no more elements in the array to search for? Should we use some kind of a counter? We certainly can use a counter, but we can implement the method without using any counter. To com- pute the middle location for the next comparison, we need two indices—low and high. The low and high indices are initialized to 0 and N 1, respectively. The middle location is computed as mid = (low + high) / 2; //the result is truncated If number[mid] is less than the search value, then low is reset to mid+1. If number[mid] is greater than the search value, then high is reset to mid1, and the search continues. Eventually, we will locate the search value or we will run out of elements to compare. We know that there are no more elements to compare when low becomes larger than high. Figure 11.3 shows how this works. Here’s the binarySearch method: public int binarySearch (int[] number, int searchValue) { int low = 0, high = number.length - 1, mid = (low + high) / 2; while (low = high number[mid] != searchValue) { if (number[mid] searchValue) { low = mid + 1; } else { //number[mid] searchValue high = mid - 1; } mid = (low + high) / 2; } if (low high) { mid = NOT_FOUND; } return mid; } 11.1 Searching 623 wu23399_ch11.qxd 12/28/06 12:41 Page 623
  • 651. 624 Chapter 11 Sorting and Searching 0 1 2 3 4 5 6 7 8 5 12 17 23 38 44 77 84 90 low0 high8 mid4 38 45 so set low mid1 1 0 1 2 3 4 5 6 7 8 44 77 84 90 low5 high8 mid6 77 45 so set high mid1 2 0 1 2 3 4 5 6 7 8 44 high5 low5 mid5 44 45 so set low mid1 3 0 1 2 3 4 5 6 7 8 low6 high5 low high so no more elements to search 4 Suppose we search for 45: Figure 11.3 How the unsuccessful search is terminated in the binary search routine. 1. Suppose an array contains 2048 elements. What are the least and the greatest numbers of comparisons for a successful search using linear search? 2. Repeat question 1 for a binary search. 11.2 Sorting In this section we will describe two basic sorting algorithms. A more advanced sorting algorithm will be presented in Section 11.3. Let’s start with the problem statement for sorting: Given an array of N values, arrange the values into ascending order. Selection Sort Given a list of integers, how would you sort them? The most natural sorting algo- rithm for a human looks something like this: 1. Find the smallest integer in the list. 2. Cross out the number from further consideration and copy it to a new (sorted) list. 3. Repeat steps 1 and 2 until all numbers are crossed out in the list. wu23399_ch11.qxd 12/28/06 12:41 Page 624
  • 652. Figure 11.4 shows this human sorting algorithm with the first three numbers being copied to the new list. We can write a real computer program based on this sorting algorithm, but the resulting program will not be a good one. There are two problems. First, we need an extra array to keep the sorted list. This may not sound like much, but when you con- sider an array of, say, 10,000 elements, using a second array is very wasteful. Sec- ond, crossing out numbers is effective for humans only. We humans can see the cross marks and will not consider the numbers once they are crossed out, but in computer programs, we still have to write code to check every element to see whether it is crossed out. We can “cross out” an element by replacing it with a neg- ative number, say, 1, if the numbers are all positive. If not, then we have to use other means to cross out an element. So crossing out the numbers does not reduce the number of comparisons in the program. Although we do not want to implement this human sorting algorithm as is, we can derive an algorithm based on the idea of finding the smallest number in a given list and moving it to the correct position in the list. This sorting algorithm is called selection sort. The selection sort is comprised of sorting passes. Figure 11.5 shows the effect of the first pass on a sample array of N ( 9) elements. First we locate the smallest 11.2 Sorting 625 0 1 2 3 4 5 6 7 8 23 17 5 90 12 44 38 84 77 0 1 2 3 4 5 6 7 8 5 12 17 Sorted list Original list Figure 11.4 Human sorting algorithm after three numbers are moved to the sorted list. selection sort sorting passes 0 1 2 3 4 5 6 7 8 5 17 Sorted Unsorted 23 90 12 44 38 84 77 0 1 2 3 4 5 6 7 8 23 17 5 90 12 44 38 84 77 Exchange start min Figure 11.5 Effect of executing the first pass in the selection sort. wu23399_ch11.qxd 12/28/06 12:41 Page 625
  • 653. element in the array and set the index min to point to this element. Then we exchange number[start] and number[min]. After the first pass, the smallest element is moved to the correct position. We increment the value of start by 1 and then execute the second pass. We start the first pass with start 0 and end the last pass with start N-2. Figure 11.6 shows the sequence of eight passes made to the sample array. Here’s the selectionSort method: public void selectionSort(int[] number) { int minIndex, length, temp; length = number.length; for (int startIndex = 0; startIndex = length-2; startIndex++){ //each iteration of the for loop is one pass minIndex = startIndex; //find the smallest in this pass at //position minIndex for (int i = startIndex+1; i = length-1; i++) { if (number[i] number[minIndex]) minIndex = i; } 626 Chapter 11 Sorting and Searching 0 1 2 3 4 5 6 7 8 23 17 5 90 12 44 38 84 77 1 0 1 2 3 4 5 6 7 8 5 17 23 90 12 44 38 84 77 2 0 1 2 3 4 5 6 7 8 5 12 23 90 17 44 38 84 77 3 0 1 2 3 4 5 6 7 8 5 12 17 90 23 44 38 84 77 4 Pass Sorted 0 1 2 3 4 5 6 7 8 5 12 17 23 90 44 38 84 77 5 0 1 2 3 4 5 6 7 8 5 12 17 23 38 44 90 84 77 6 0 1 2 3 4 5 6 7 8 5 12 17 23 38 44 90 84 77 7 0 1 2 3 4 5 6 7 8 5 12 17 23 38 44 77 84 90 8 Pass Figure 11.6 Eight passes to sort the sample array of nine elements. wu23399_ch11.qxd 12/28/06 12:41 Page 626
  • 654. //exchange number[startIndex] and number[minIndex] temp = number[startIndex]; number[startIndex] = number[minIndex]; number[minIndex] = temp; assert minStart(number, startIndex): Error: + number[startIndex] + at position + startIndex + is not the smallest.; } assert isSorted(number): Error: the final is not sorted; } The assertion at the end of one pass confirms that the smallest element in that pass moved to the beginning position of the pass. The minStart method is therefore written as follows: private boolean minStart(int[] number, int startIndex) { for (int i = startIndex+1; i number.length; i++) { if (number[startIndex] number[i]) { return false; } } return true; } We put a second assertion at the end of the method to verify that no elements are out of place after the sorting is complete. The isSorted method is written as follows: private boolean isSorted(int[] number) { for (int i = 0; i number.length-1; i++) { if (number[i] number[i+1]) { return false; } } return true; } Assertion is a very useful tool in a situation such as sorting. While developing the sorting routines, we insert a number of assertion statements to increase our con- fidence in the program’s correctness. 11.2 Sorting 627 wu23399_ch11.qxd 12/28/06 12:41 Page 627
  • 655. Let’s analyze the selection sort algorithm. In analyzing different sorting algo- rithms, we normally count two things: the number of comparisons and the number of data movements (exchanges). We will show you how to count the number of comparisons here. Counting the number of data movements is left as Exercise 4. Keep in mind that the analysis we provide in this chapter is an informal one. A detailed analysis is beyond the scope of this book, so we will give only a taste of the formal analysis. The selection sort has one comparison (the if statement inside the nested-for loop), so we can easily count the total number of comparisons by counting the num- ber of times the inner loop is executed. For each execution of the outer loop, the inner loop is executed length start times. The variable start ranges from 0 to length-2. So the total number of comparisons is computed by finding the sum of the right column in the following table: Number of Comparisons Start (Length Start) 0 length 1 length – 1 2 length – 2 . . . . . . length – 2 2 628 Chapter 11 Sorting and Searching Be sure to run programs with assertions enabled during the development, but disable them during the actual use.Run the program with assertions enabled with java -ea main class We use assertions to find coding error. But what will happen when the code we write for assertions, such at the minStart method used in the selection sort routine, is wrong? How can we assert that minStart is correct? We do not want to write assertions for assertions! One possibility is to create a data set that is correct and to run the minStart method against these data to test for their validity. The use of assertions is merely an aid, not a fail-safe way to find errors. wu23399_ch11.qxd 12/28/06 12:41 Page 628
  • 656. The variable length is the size of the array. If we replace length with N, the size of the array, then the sum of the right column is N (N 1) (N 2) . . . 2 N i2 i N i1 i 1 N(N 2 1) 1 N2 2 N 2 N2 The total number of comparisons is approximately the square of the size of an array. This is a quadratic function, so the number of comparisons grows very rapidly as the size of an array increases. Is there a better sorting algorithm? The answer is yes. Bubble Sort The effect of one pass of the selection sort is the movement of the smallest element to its correct position. Since an array gets sorted only by moving the elements to their correct positions, the whole sorting routine will complete sooner if we increase the number of data movements. In the selection sort, we make one exchange per pass. If we could move more elements toward their correct positions in one pass, we could complete the sorting sooner than the selection sort. The bubble sort is one such algorithm that increases the number of data movements for the same number of comparisons as the selection sort makes. The key point of the bubble sort is to make pairwise comparisons and to ex- change the positions of the pair if they are out of order. Figure 11.7 shows the effect of pairwise comparisons in the first pass of the bubble sort. After the first pass, the largest element, 90, has moved to its correct position in the array. This is the guar- anteed effect of one pass. In addition, we notice that many other elements have moved toward their correct positions, as bubbles move toward the water’s surface. In the worst case, the bubble sort will make N 1 passes, so the worst-case performance is the same as that for the selection sort. However, in the average case, we can expect a better performance from the bubble sort. The bubble sort exhibits two properties: • After one pass through the array, the largest element will be at the end of the array. • During one pass, if no pair of consecutive entries is out of order, then the array is sorted. Using these properties, we can express the bubbleSort method in pseudocode: bottom = number.length - 2; exchanged = true; while (exchanged) { //continue if the exchange is made //do one pass of sorting exchanged = false; //reset the variable 11.2 Sorting 629 This while loop per- forms at most N1 passes for an array with N elements.The loop will terminate when there are no ex- changes in one pass. wu23399_ch11.qxd 12/28/06 12:41 Page 629
  • 657. 630 Chapter 11 Sorting and Searching 0 1 2 3 4 5 6 7 8 23 17 5 90 12 44 38 84 77 0 1 2 3 4 5 6 7 8 17 23 5 90 12 44 38 84 77 0 1 2 3 4 5 6 7 8 17 5 23 90 12 44 38 84 77 Exchange Exchange 0 1 2 3 4 5 6 7 8 17 5 23 12 90 44 38 84 77 Exchange 0 1 2 3 4 5 6 7 8 17 5 23 12 44 90 38 84 77 Exchange 0 1 2 3 4 5 6 7 8 17 5 23 12 44 38 90 84 77 Exchange 0 1 2 3 4 5 6 7 8 17 5 23 12 44 38 84 90 77 Exchange 0 1 2 3 4 5 6 7 8 17 5 23 12 44 38 84 77 90 Exchange Notice how the value 90 migrates toward its correct position. In addition, other values also move toward their correct positions. Effect: The largest element moves to its correct position. Use an assertion to verify this condition. Figure 11.7 Effect of executing the first pass in the bubble sort. wu23399_ch11.qxd 12/28/06 12:41 Page 630
  • 658. for (int i = 0; i = bottom; i++) { //pairwise comparison if (number[i] number[i+1]) { //the pair is out of order exchange them; exchanged = true; //an exchange is made } } //one pass is done, decrement the bottom index by 1 bottom--; } Translating the pseudocode into an actual method, we have public void bubbleSort(int[] number) { int temp, bottom; boolean exchanged = true; bottom = number.length - 2; while (exchanged) { exchanged = false; for (int i = 0; i = bottom; i++) { if (number[i] number[i+1]) { temp = number[i]; //exchange number[i] = number[i+1]; number[i+1] = temp; exchanged = true; //exchange is made } } assert maxBottom(number, bottom): Error: + number[bottom] + at position + bottom + is not the largest.; bottom--; } assert isSorted(number): Error: the final is not sorted; } The maxBottom method verifies that the largest element among elements from position 0 to bottom is at position bottom. The method is written as follows: private boolean maxBottom(int[] number, int lastIndex) { for (int i = 0; i lastIndex; i++) { 11.2 Sorting 631 One pass of bubble sort Assert the element at posi- tion bottom is the largest among elements from position 0 to bottom. wu23399_ch11.qxd 12/28/06 12:41 Page 631
  • 659. if (number[lastIndex] number[i]) { return false; } } return true; } On average, we expect the bubble sort to finish sorting sooner than the selec- tion sort, because there will be more data movements for the same number of comparisons, and there is a test to exit the method when the array gets sorted. The worst case of the bubble sort happens when the original array is in descending order. Notice that if the original array is already sorted, the bubble sort will perform only one pass whereas the selection sort will perform N 1 passes. 632 Chapter 11 Sorting and Searching 1. Show the result of the second pass of bubble sort applied to the array at the bottom of Figure 11.7. 2. For an array with N elements, what is the least number of comparisons the bubble sort will execute? 11.3 Heapsort Selection and bubble sorts are two fundamental sorting algorithms that take approximately N2 comparisons to sort an array of N elements. One interesting sorting algorithm that improves this performance to approximately 1.5N log2 N is heapsort. We will describe the heapsort algorithm and analyze its performance in this section. The heapsort algorithm uses a special data structure called a heap. A heap consists of nodes, which contain data values, and edges, which link the nodes. Figure 11.8 shows a sample heap. We use integers as data values for the examples in this section. The topmost node is called the root node of a heap. Nodes in a heap heapsort heap root node 90 84 44 77 12 5 38 17 23 7 3 4 5 6 1 2 0 8 Index Root Left child of 44 Right child of 44 Figure 11.8 A sample heap that includes nine nodes. wu23399_ch11.qxd 12/28/06 12:41 Page 632
  • 660. are indexed 0, 1, 2, and so forth in the top-to-bottom, left-to-right order, starting from the root. A node in a heap has zero, one, or two children. The children of a node are distinguished as the node’s left and right children. If a node has only one child, then it is the left child of the node. A heap must satisfy these two constraints: 1. Structural constraint. Nodes in a heap with N nodes must occupy the positions numbered 0, 1, . . . , N 1. Figure 11.9 shows examples of nonheaps that violate the structural constraint. 2. Value relationship constraint. A value stored in a node must be larger than the maximum of the values stored in its left and right children. Figure 11.10 shows examples of nonheaps that violate the value relationship constraint. 11.3 Heapsort 633 left and right children heap constraints Heaps Nonheaps Figure 11.9 Sample heaps and nonheaps that violate the structural constraint. 45 22 16 12 3 45 25 45 9 34 22 11 45 12 45 12 24 16 13 90 35 45 58 16 12 3 45 55 45 22 33 23 34 45 25 45 22 55 12 3 45 25 Heaps Nonheaps Figure 11.10 Sample heaps and nonheaps that violate the value relationship constraint.Violations are indicated by blue ellipses. wu23399_ch11.qxd 12/28/06 12:41 Page 633
  • 661. How can we use the heap structure to sort N elements? Heapsort is carried out in two phases: 1. Construction phase. Construct a heap given N elements. 2. Extraction phase. Pull out the value in the root successively, creating a new heap with one less element after each extraction step. We will begin the description of heapsort from the extraction phase. Consider the heap shown in Figure 11.8. Since every node in the heap satisfies the value rela- tionship constraint, we can conclude that the value in the root node is the largest among the values stored in the heap. Now, after we remove the value 90 from the heap, we must create a new heap that has one less element. We can build such a heap by first moving the last element (value 23 in the figure) to the root position. With the value 23 in the root position, we have met the structural constraint for the heap with eight elements. However, the value relationship constraint is not met. The violation occurs because 23 is smaller than the larger of its two children. By swapping 84 and 23, the violation is eliminated. Since the value 23 is now at a new location, we must check again if the violation occurs at this position. It does, because 77 is larger than 23, so we swap again. We repeat the process until either there are no more children to consider or the value moved into a new position meets the value relationship constraint. We will call this process a rebuild step. One rebuild step is illustrated in Figure 11.11. Using a heap with N elements, we can sort the given N elements by perform- ing the rebuild steps N 1 times. Figure 11.12 illustrates the rebuild steps for the sample heap. Notice how the array for the sorted list is filled from the end. All we have to do now is to figure out how to build a heap from a given unsorted list of N elements. Let’s study the construction phase of the algorithm. We will illustrate the construction phase with the following unsorted nine elements: 23, 17, 5, 90, 12, 44, 38, 84, 77 If we assign the given numbers to a heap structure in ascending index order, we have the heap structure shown in Figure 11.13. This heap structure is not truly a heap because it violates the value relationship constraint. The construction phase will eliminate any violations of the value relationship constraint. The key concept for the construction phase is the rebuild step we used in the extraction phase. We will build a complete heap in a bottom-up fashion. We start out with a small heap and gradually build bigger and bigger heaps until the heap contains all N elements. Figure 11.14 shows the sequence of rebuild steps. The triangles indicate where the rebuild steps are applied. In the extraction step, the rebuild step is always applied to the root node. In the construction step, each rebuild step is applied successively, be- ginning with the node at index (N 2)2 and ending with the node at index 0 (i.e., the root node). Now let’s consider how we can implement this algorithm in Java. We must first decide how to represent a heap. Among the possible alternatives, the data structure we learned in this book that can be used here very effectively is an array. 634 Chapter 11 Sorting and Searching heapsort phases rebuild step wu23399_ch11.qxd 12/28/06 12:41 Page 634
  • 662. 11.3 Heapsort 635 0 1 2 3 4 5 6 7 8 90 M ove M ove 77 7 3 4 5 0 6 8 17 77 17 1 2 84 12 5 44 90 23 38 7 3 4 5 0 6 84 1 2 77 12 5 44 38 7 3 4 5 0 6 23 1 2 84 12 5 44 38 23 max{84, 44} ? NO, so swap 7 3 4 5 0 6 77 17 23 17 23 17 84 1 2 23 12 5 44 38 23 max{77, 12} ? NO, so swap 7 3 4 5 0 6 84 1 2 77 12 5 44 38 23 max{17} ? YES, so stop A new heap with one fewer element. Figure 11.11 One rebuild step after the value 90 is pulled out from the heap. The net result of a single rebuild step is a new heap that contains one fewer element. wu23399_ch11.qxd 12/28/06 12:41 Page 635
  • 663. 0 1 2 3 4 5 6 7 8 90 77 7 3 4 5 0 6 8 17 23 1 2 84 12 5 44 38 0 1 2 3 4 5 6 7 8 90 84 23 7 3 4 5 0 6 17 1 2 77 12 5 44 38 0 1 2 3 4 5 6 7 8 90 84 77 17 3 4 5 0 6 1 2 23 12 5 44 38 0 1 2 3 4 5 6 7 8 90 84 77 44 17 3 4 5 0 1 2 23 12 5 38 1 0 1 2 3 4 5 6 7 8 90 84 77 44 38 17 3 4 0 1 2 23 12 5 5 0 1 2 3 4 5 6 7 8 90 84 77 44 38 23 12 3 0 1 2 17 5 6 0 1 2 3 4 5 6 7 8 90 84 77 44 38 23 17 0 1 2 12 5 7 0 1 2 3 4 5 6 7 8 90 84 77 44 38 23 17 12 0 1 5 8 0 1 2 3 4 5 6 7 8 90 84 77 44 38 23 17 12 5 0 9 2 3 4 No rebuild is necessary here. 90 84 23 17 77 44 12 5 38 Figure 11.12 Eight rebuild steps to sort a heap with nine elements. 636 wu23399_ch11.qxd 12/28/06 12:41 Page 636
  • 664. Figure 11.15 shows the correspondence between the heap and the array representa- tion. An important aspect in deciding which data structure to use is the ease of locating a given node’s left and right children. With an array implementation, we can locate any node’s left and right children easily. A node with index I has its left child at index 2I 1 and its right child at index 2I 2. Since the heapsort algorithm is more involved than the insertion or bubble sort algorithm, we will put all the necessary code in a single class called Heap to provide a complete picture. To simplify our implementation so we can focus on the algo- rithm, we will allow only integers. You can modify the class to allow any objects to be sorted; see Exercise 9 at the end of this chapter. The following code illustrates how to use the Heap class: int[ ] number = { 90, 44, 84, 12, 77, 23, 38, 5, 17 }; int[ ] sortedList; Heap heap = new Heap( ); heap.setData(number); //assign the original list sortedList = heap.sort( );//sort the list for (int i = 0; i sortedList.length; i++) { //print out System.out.print( + sortedList[i]); //the sorted } //list The Heap class will include two arrays as its data members: one to implement a heap and another to store the sorted list. /** * This class implements the heapsort algorithm. This class * can sort only integers. */ class Heap { 11.3 Heapsort 637 23 17 5 90 12 44 38 84 77 7 3 4 5 6 1 2 0 8 Figure 11.13 A heap structure with given numbers assigned in ascending index order. wu23399_ch11.qxd 12/28/06 12:41 Page 637
  • 665. 638 Chapter 11 Sorting and Searching 90 7 3 4 5 0 6 8 1 First rebuild at position 3. 84 77 23 1 2 17 12 44 5 38 90 7 3 4 5 0 6 8 3 Third rebuild at position 1. 84 77 23 1 2 17 12 5 44 38 77 7 3 4 5 0 6 8 17 23 90 1 2 84 12 5 44 38 84 7 3 4 5 0 6 8 4 Fourth rebuild at position 0. 17 77 23 1 2 90 12 5 44 38 90 7 3 4 5 0 6 8 2 Second rebuild at position 2. 84 77 23 1 2 17 12 44 5 38 Figure 11.14 Sequence of rebuild steps applied in the construction phase.Rebuild steps are carried out at index positions 3,2,1,and 0. wu23399_ch11.qxd 12/28/06 12:41 Page 638
  • 666. /** * Implements the heap */ private int[ ] heap; /** * Stores the sorted list */ private int[ ] sortedList; // methods come here ... } Now let’s look at the methods. The setData method initializes the two data members as follows: public void setData(int[ ] data) { heap = new int[data.length]; sortedList = new int[data.length]; for (int i = 0; i data.length; i++) { heap[i] = data[i]; } } Notice that we copy the contents of the data array to the heap array. If we simply assign the parameter to the data member heap as heap = data; then all we are doing is setting two names referring to the same object. Since we do not want to change the original data array, we make a separate copy. 11.3 Heapsort 639 90 Heap Array implementation 84 44 77 12 5 38 17 23 7 3 4 5 6 1 2 0 8 0 1 2 3 4 5 6 7 8 90 84 44 77 12 5 38 17 23 Figure 11.15 A sample heap and the corresponding array implementation. wu23399_ch11.qxd 12/28/06 12:41 Page 639
  • 667. The sort method calls two private methods that implement the two phases of the heapsort algorithm: public int[ ] sort( ) { construct( ); //perform the construction phase extract( ); //perform the extraction phase return sortedList; } Here’s the construct method: private void construct( ) { int current, maxChildIndex; boolean done; for (int i = (heap.length-2) / 2; i = 0; i--) { current = i; done = false; while (!done) {//perform one rebuild step //with the node at index i if (2*current+1 heap.length-1) { //current node has no children, so stop done = true; } else { //current node has at least one child, //get the index of larger child maxChildIndex = maxChild(current, heap.length-1); if (heap[current] heap[maxChildIndex]) { //a child is larger, so swap and continue swap(current, maxChildIndex); current = maxChildIndex; } else { //the value relationship constraint //is satisfied, so stop done = true; } } } assert isValidHeap(heap, i, heap.length-1): Error: Construction phase is not working + correctly; } 640 Chapter 11 Sorting and Searching wu23399_ch11.qxd 12/28/06 12:41 Page 640
  • 668. testPrint(heap.length ); //TEMP } The isValidHeap method is used to assert that elements from position start to posi- tion end form a valid heap structure. Here’s the method: private boolean isValidHeap(int[] heap, int start, int end) { for (int i = start; i end/ 2; i++) { if (heap[i] Math.max(heap[2*i+1], heap[2*i+2])) { return false; } } return true; } And here’s the extract method: private void extract( ) { int current, maxChildIndex; boolean done; for (int size = heap.length-1; size = 0; size--) { //remove the root node data sortedList[size] = heap[0]; //move the last node to the root heap[0] = heap[size]; //rebuild the heap with one fewer element current = 0; done = false; while (!done) { if (2*current+1 size) { //current node has no children, so stop done = true; } else { //current node has at least one child, //get the index of larger child maxChildIndex = maxChild(current, size); if (heap[current] heap[maxChildIndex]) { //a child is larger, so swap and continue swap(current, maxChildIndex); current = maxChildIndex; 11.3 Heapsort 641 wu23399_ch11.qxd 12/28/06 12:41 Page 641
  • 669. } else { //value relationship constraint //is satisfied, so stop done = true; } } } assert isValidHeap(heap, i, heap.length-1): Error: Construction phase is not working + correctly; testPrint( size ); //TEMP } } A number of methods are shared by both methods. The maxChild method returns the index of a node’s left or right child, whichever is larger. This method is called only if a node has at least one child. The first parameter is the index of a node, and the second parameter is the index of the last node in a heap. The second parameter is necessary to determine whether a node has a right child. The method is defined as follows: private int maxChild(int location, int end) { int result, leftChildIndex, rightChildIndex; rightChildIndex = 2*location + 2; leftChildIndex = 2*location + 1; //Precondition: // Node at 'location' has at least one child assert leftChildIndex = end: Error: node at position + location + has no children.; if (rightChildIndex = end heap[leftChildIndex] heap[rightChildIndex]) { result = rightChildIndex; } else { result = leftChildIndex; } return result; } The other two methods shared by the construct and extract methods are swap and testPrint. The swap method interchanges the contents of two array elements, and the testPrint method outputs the heap array for verification and debugging pur- poses. You can comment out the calls to testPrint from the construct and extract 642 Chapter 11 Sorting and Searching wu23399_ch11.qxd 12/28/06 12:41 Page 642
  • 670. methods after you verify that the algorithm is implemented correctly. Here are the two methods: private void swap (int loc1, int loc2) { int temp; temp = heap[loc1]; heap[loc1] = heap[loc2]; heap[loc2] = temp; } private void testPrint(int limit) { for (int i = 0; i limit; i++) { System.out.print( + heap[i]); } System.out.println( ); } There are several improvements we can make to the simple Heap class we provided here. These improvements are left as Exercise 9. Performance How good is the heapsort? We mentioned in Section 11.2 that the performances of both selection and bubble sort algorithms are approximately N2 comparisons for sorting N elements. The heapsort algorithm is substantially more complex in design and implementation than the other two basic sorting algorithms. Is the extra complexity worth our effort? The answer is yes. The performance of the heapsort algorithm is approximately 1.5N log2 N comparisons for sorting N elements. This is a remarkable improvement. Consider the difference between the two per- formances for large N. For example, to sort 100,000 elements, the selection or bubble sort requires 10,000,000,000 comparisons, while the heapsort requires only 1.5 100,000 log2 100,000 2,491,695 comparisons. If a single comparison