Jia Object Oriented Software Development Using Java Principles Patterns and Frameworks 2nbsped 0201737337
Jia Object Oriented Software Development Using Java Principles Patterns and Frameworks 2nbsped 0201737337
_eRI_NcLeLEs__ _ _
Xiaoping Jia
DePaul University
~
.
Addison
WPsl<•y
Acces the latest information about Addison-Wesley titles from our World Wide Web site:
www.aw.com/cs
Many of the designations used by manufacturers and sellers to distinguish their products are
claimed as trademarks. Where those designations appear in this book, and Addison-Wesley
was aware of a trademark claim, the designations have been printed in initial caps or all caps.
Toe programs and applications presented in this book have been included for their instructional
value. They have been tested with care, but are not guaranteed for any particular purpose. The
publisher does not offer any warranties or representations, nor does it accept any liabilities
with respect to the programs or applications.
Credits: Figure 1.2: Kruchten, Rational Unified Process 2nd ed., Fig. 2.2 (p. 23), © 2000
Addi.son Wesley Longman Inc. Reprinted by permission of Pearson Education, Inc. Figure
3.1: Riggs et al, Programming Wireless Devices w/Java 2 Platform, Micro Edition, Fig. 2.1
(p. 8), © 2001 Sun Microsystems Inc. Reprinted by permission of Pearson Education, Inc.
For information on obtaining permission for the use of material from this work, please submit
a wrinen request LO Pearson Education, Inc., Rights and Contracts Department, 75 Arlington
SL, Suite 300, Boston, MA 02116 or fax. your request to 617-848-7047.
All rights reserved. No part of this publication may be reproduced, stored in a retrieval system,
or transmitted, in any form or by any means, electronic, mechanical, photocopying, recording,
or otherwise, without the prior written permission of the publisher. Printed in the United States
of America.
2 3 4 5 6 7 8 9 10- HT-05 04 03
To Ai-Ling and Robin
B•,ZiiiiiH
Preface xv
Glossary 653
References 663
Index 667
Object-Oriented Software
Development
CHAPTER OVERVIEW
In this chapter, we provide an overview of object-oriented software development. We
start w ith a general d iscussion of software development processes and the desi@ble
qualities of software products. Next, we discuss what makes software development
difficult and the difference between software engineering and other more established
engineering practices. Then we take a close look at ite@tive software development
processes, including the Rational Unified Process (RUP) and Extreme Programming (XP).
1
2 ■ Object-Oriented Software Development
• On January 15, 1990, the AT&T long-distance telephone network broke down,
interrupting nationwide long-distance telephone services in the United State f or
more than 8 hours. An ill-placed break statement in the switching software,
written in the C language, was to blame for the breakdown.
• On June 4, 1996, the maiden flight of the new and improved Ariane 5 commu
nication satellite launcher developed by the European Space Agency exploded
37 seconds after liftoff. An incorrectly handled software exception resulting from
converting a 64-bit floating point to a 16-bit signed integer caused the disaster.
• On June 8, 200 I, a software problem in the new trading software installed
overnight for the New York Stock Exchange caused failures in trading on half of
the floor of the exchange and forced the NYSE to shut down the entire trading
floor for more than an hour.
Although uch cata trophic failures are rare, minor glitches are common in almost all
oftware. In other words, buggy software is the nom1.
However, the state of oftware development practice i far from the "software
crisis" many have proclaimed in the past. Advances in many aspects of software de
velopment methodologies and software engineering proce ses have made it po ible
I? develop many large-scale software systems that perform as expected most of the
time. We are not capable of delivering nor required to deliver 100% reliable software
The question i , How good i good enough?
1.1 The Challenges of Software Development ■ 3
Complexity The software ystem being developed today are often very large and
complex. Complexity is dictated by the problems the sy tem are intended to solve
and the services they are intended to provide. From the engineering perspective, both
requirements are often beyond the control of software developers. The complexity
involved in a large-scale software system is so great that no individual can comprehend
every detail of the system. To build such a complex sy tem, it mu t be broken down
into manageable parts and requires the cooperative effo11s of a team of developer
rather than the efforts of an individual. Methodologie , techniques, and tool that
work well for small systems developed by individuals usually are not effective for
large systems developed by tean1s.
High User Expectations In the past, whe n computer were mainly u ed in univer-
sitie , research institutions, and large corporation , the majority of software ystem
users were scientists and engineer who had the technical kill 10 handle glitche
they might encounter while using the sy tern . Today, computer are used in home ,
school , and businesses of all size , and are used for plea ure a well a for work.
The majority of today's software u er are nontechnical, ordinary people. Computer
oftware products are con idered more and more like con umer producl and are ex-
pected to perfom1 with the a me depe ndability as hou ehold appliances. Occa ional
glitche that once were con idered acceptable are now intolerable. Software Y tern
are expected to be " bug-free ," but uch perfection i next to impossible.
I>
AN ENGINEERING PERSPECTIVE
..i
The term software engineering was coined al a NATO workshop in 1968. It repre
ented an aspiration to m ch e practice of software development on a solid scientific
foundation and to attain the level of reliability and productivity associated with well
e tabli hed engineering di cipline , uch as civil and mechanical engineering.
Software engineering is �jneering discipline concerned with al�
developing and delivering high-quality and use�i:�ecti-ve-manner.
Software engineering defines the various activities in the software development and
the products, or deliverables, a sociated with these activities. Software engineering
also define the sofnvare development processes, which define the order for carrying
out the development activities and the criteria for the deliverables of the activities.
components, and detail design, which is primarily concerned with the solutions to
each component. Software designs are often documented using various diagrams.
Integration and System Testing The individual components or units are integrated
and tested as a whole to ensure that the entire software system functions properly with
respect to its specification.
The most well-known software development process is the ·<waterfall" model illus-
trated in Figure 1.1, which has been the de facto standard of the software development
process. In the waterfall model, the development activities are carried out in succes-
sive phases linearly: requirements analysis, design, implementation and unit te ting,
integration and system testing, and maintenance. A phase i the pan of time between
two major milestones of the process in which a well-defined et of objective are met,
artifacts are completed, and decisions are made whether to move into the next phase.
In principle, the deliverable of each phase must be approved (" igned off') before the
Figure 1.1
Requirements
l
analysis
The waterfall model
of software devel- +
I
I
opment. '---- - Design
+
,' _____
I
i
Implementation
and unit testing
+
I
I
i
Integration and
·----- system testing
+
I
,_____
I
Maintenance
as
next phase can begin. The rationale is that changes to the requirements specification
cost much less to implement in the requirements analysis than in the later phases. The
later the phase in which a change to the requirements is introduced, the more it costs.
So the goal is to minimize changes after the documents are delivered. This requires
that the tasks of each phase be completed thoroughly, and that the deliverables of each
phase be frozen once they are delivered and approved.
However, the waterfall model is not realistic. It is very common that changes
occur during every phase of the development process. The changes may come from a
number of sources: errors or faults of the specification and design may be discovered
during implementation; assumptions made in design may be proven false during
y tern testing; some features requested by the customers may be proven to be too
low, excessive in resource consumption, or infeasible during system testing; and
user needs and requirements may have changed after the requirements analysis is
completed. Therefore, in practice, it is often necessary to have several iterations of
the phase in the waterfall model. However, one of the major shortcomings of the
waterfall model is that it does not facilitate such iterations.
There are several alternative software development processes that are designed
to carry out the software development activities in an iterative fashion. The itera-
tive software development processes are becoming popular and gaining acceptance
in practice, partly because of the wide acceptance of object-oriented development
methodologies, which are especially suited to iterative development. We will discuss
two of the common iterative development processes in Section 1.4.
N?t all of these desir~ble qualities are attainable at the same time, nor are they of
equal importance. A crucial part of software development is dealing with the trade.-
o~s amo?g these different qualities to achieve a reasonable balance. Obviously, the
obJect-on:nted developm~ t approach cannot directly improve aJI these qualities. It
~cuse~ primarily O!!_improving_the_!!laintainability and reusabj lity of software sys- ,
terns.Maintainability should be the focuo fife development process for three main
reasons. First, for software systems with long lifetimes, maintenance co ts will far ex-
ceed initial development costs. It is imprudent to compromise maintainability because
any savings that may result ini tial ly will undoubtedly be dwarfed by maintenance cost
penalties over the long run. Second, current development technology does not yield
high reliability in the initial release of software sy tern . Reliability i u ually attained
through repeated corrections during the development pha e and throughout the life-
times of software system . Software system reliability can be everely hampered by
poor maintainabi lity. Third, high maintainability require flexibility in the de ign and
implementation of software systems. Such flexibility facilitate the kind of incremen-
tal development that enhances reliability, u efulne s, and u er friendline . a well as
the ability to contain costs.
Several factors contribute to the maintainability of oftware y tern :
These fac tors me the fo us of our discu. sion of methods and techniques in later
chapters.
8 ■ Object-Oriented Software Development
Analysis of Designs
Over the centuries, craftsmanship clearly has proved capable of building magnificent
tructures. such as the Egyptian pyramids, Roman aqueducts, and Notre Dame Cathe-
dral. However, modem engineering offers assurance, predictability, and efficiency
that craftsmanship cannot match. One of the key differences between engineering
and craftsmanship is that the success of engineering projects can be assured before-
hand through scientific analysis of their designs, whereas the success of craftsmanship
projects is attained through trial and error during current and prior construction.
Civil engineers depend on mechanics to help them predict with confidence before
construction begins that a newly designed bridge or building will stand and function
as it is supposed to. Aerospace engineers depend on aerodynamics and simulation to
help them predict with confidence before it is built that a newly designed airplane
will fly.
In contrast, software developers largely depend on testing and debugging (i.e.,
trial and error) to establish confidence in their products. Software development is
like building modern skyscrapers with craftsmanship, with the success of software
development projects rarely assured beforehand.
Nonrecurrence of Failures
Failures, ometimes catastrophic, also occur in well-established engineering fields.
Perhaps one of the most spectacular failures in the history of engineering was the
collapse of the Tacoma Narrows Bridge in 1940. Its design was unconventional and
innovative, and the bridge was dramatic and elegant in appearance. Careful analysis
w~s per~o~ed to en ure that the bridge would behave well under its own weight
with ant1c1pated traffic load and winds as high as 45 miles per hour. However the
~esigner did nor fore~ee that the slender ~ridge deck would act like an airplane ~ing
ma moderate crosswind of less than 40 miles per hour, which twisted the bridge apart.
A oo~ a th~ cause of the collapse _was known, 1~easures were developed to prevent
such failures in the fu1ure. Hence, rn well-established engineering fields, the same
type of failure is rarely repealed.
1.3 Object Orientation ■ 9
In software development, the same types of failures recur all the time. Few
practical measures can be taken to ensure the absence of certain types of faults in
software systems. The sad truth about software development is that no one can ensure
that the type of failure that occurred in Ariane 5 will never occur again.
Codification of Knowledge
The success of well-established engineering fields is due largely to the accumulation
and codification of knowledge and the reuse of prior solutions. Design knowledge
and solutions often are organized and presented in manuals and handbooks to make
common and routine design not only easier and faster, but also more reliable, depend-
able, and manageable. Designers often find solutions inhandbooks and then adapt and
assemble the solutions to their specific design problems. Only rarely are original and
innovative solutions needed. Usually, the codified knowledge includes what to avoid
as well as what to do.
In software development, although a lot of design knowledge and experience has
been accumulated, very little has been systematically codified. Without the benefit of
prior design solutions, each design of a software system i treated as an original.
Therefore, it is no surprise that software design is difficult, time-consuming, and
unreliable.
Thus, software development is quite different from the traditional engineering
disciplines. At best, it is an immature engineering discipline. For software develop-
ment to become a true engineering discipline, software developers must have mech-
anisms to carry out the analysis of designs, ensure nonrecurrence of knm,vn failures,
and codify design knowledge.
The main goal of software development i to build soft\ are y tern that provide
services to people and enhance their abilitie to . olve problem in the real world. A
software system usually con i ts of two e ential component : a model, which is a
representation of a pertinent part of the real world, and an algorithm, which captures
the computation involved in manipulating or proce ing the model.
Software system
Abstraction
Model Algorithm
Interpretation
10 ■ Object-Oriented Software Development
The real world is enonnous and complex. Many of its aspects are fuzzy, unknown,
or intangible. 1n contrast, the programming models used in software systems must be
precise ~d relatively small. A model is necessarily a~ ~bstractio11 of the real world.
It capture only the essential and relevant charactenst1cs of the real wo_rld from a
particular perspective and ignores others. Models are intended to be manipulated or
proce ed. and their behaviors should mimic those of the rea~ worl_d to reflect the
cho en perspectives reasonably accurately. The results of mampulations can be fed
back to the real world through i11terpretatio11 (i.e., the assignment of meanings to the
entitie in the models) and often represent solutions to real-world problems.
Programming languages are the main tools used by software developers to de-
scribe computer models. The evolution of progranuning languages and progranuning
methodologies is driven by the need to build more and more sophisticated and ef-
fective models. That need in turn is driven by the ever-increasing power of modern
computers and the desire to utilize this power.
One of the fundamental problems in software development is, How does someone
model the real world? The answer largely depends on the problems to be solved. One
way to look at the evolution of software development methodologies is through the
changing views of programming models.
In the 1950s and 1960s, the focus of software developers was on the algorithm. As
a result, the main concerns at that time were solving computation problems, designing
efficient algorithms, and controlling the complexity of computation. The models used
were computation-oriented models, and the decomposition of complex systems was
primarily based on control flo w.
In the 1970s and 1980s, different types of models emerged to address the com-
plexity of the data being processed. These systems were centered on data entities
and data fl ows, with computation becoming a secondary concern. The models used
were data-oriented models, and the decomposition of complex systems was primarily
based on data flow.
Object-oriented models represent a balanced view of the data and computation
aspects of software systems. Object-oriented models are composed of objects, which
contain data and the associated computations. The decomposition of complex systems
is based on the structure of objects, classes, and the relationships among them.
The origin of object-oriented software development dates back to the late 1960s. A
computer simulation language called Simula wa,; the first programming language that
included ome important features of object-oriented programming, such as class. The
fir t full -blown and perhaps the best known object-oriented programming language
was Smalltalk, developed by Xerox PARC in the I 970s. Object-oriented technology
grew tremendously during the 1980s, with the emergence of several more sophi _
1.4 Iterative Development Processes ■ 11
■ Each iteration is relatively small and can be completed in a relatively short period
of time.
■ Each iteration results in a release of an executable product or component, which
is a part of the final product.
The final product is developed incrementally from iteration to iteration.
5. Design model: Establishes the vocabulary of the problem and its solution
6. Process model (optional): Establishes the system's concurrency and synchro-
nization mechanisms
7. Deployment model: E tablishes the hardware topology on which the system is
executed
8. Implementation model: Establishes the parts used to assemble and release the
physical sy tem
9. Test model: Establishes the paths by which the system is validated and verified
The RUP i use case driven . Use cases defined for system requirements are the
foundation for all other development activities, including design, implementation,
and te ting. The RUP is architecture centric. The main focus of early iteration of the
development process is to produce and validate an executable architecture prototype,
which gradually evolves to become the final system in later iterations.
The process structure of the RUP can be illustrated in a two-dimensional chart
as hown in Figure 1.2. One dimension represents the time in terms of phases and
itera.tion . The other dimension represents the process work.flows. The chart shows
roughly the amount of time or attention devoted to each process work.flow during
various phases and iterations.
A process workjl.oH consists of a sequence of activities that produce a set of
anifacts, or deliverables, which can be project plans, design models, source code,
tests, and documentations. The RUP defines nine process work.flows:
Environment ~~====::~r;::;::~~==~:,,-::=======-4r========--
lnitlal
~ - - - ~ - ~ ~ #1
I
IElabllElabl 1cons111c onstj[Const j
#2 #N
[l'ra;,1
~ #2
1Tran \
Iterations
1.4 Iterative Development Processes • 15
4. Implementation: Takes into account software development, unit test, and integra-
tion
5. Test: Describes test cases, procedures, and defect-tracking metrics
6. Deployment: Covers the deliverable ystem configuration
7. Con.figuration management: Controls change to and maintains the integrity of a
project's artifacts
8. Project management: Describes various strategies of working with an iterative
process
9. Environment: Covers the necessary infrastructure required to develop a system
Each phase is further broken down into one or more irerations. Each iteration goes
through the various process workflows (de cribed earlier) and i a complete develop-
ment cycle that results in the release of an executable product. The phase serve as the
controlling framework of the iterations. Iteration in different phases have different
emphases on process workflows as illu trated in Figure 1.2. For example, iteration
in the inception phase focus more on business modeling and requirements, while it-
erations in the construction phase focu more on implementation and configuration
management.
refactoring to maintain and improve qualities and to facilitate changes and enhance-
ments.
The core of XP consists of the following key practices:
Planning game: Start with a simple a plan for each iteration, and continually
refine the plan as necessary.
Frequenr and small releases: Make frequent and small releases starting as early
as possible. Each iteration, that is, the duration for producing a release,
bould not be longer than a few weeks.
Metaphor: U e metaphor to start development and communicate with the
customers.
Simple design: Make design as simple as possible. Refactor later if changes are
nece sary.
Test first: Write unit test before writing code.
Refactoring: Refactor to make the system simpler and clearer or to reduce
duplication.
Pair programming: Write all production code in pairs.
Collective ownership: Anyone may change code anywhere in the system to
improve it.
Continuous imegrarion: Integrate as soon as a task is complete.
40-hourweek: Teams are more productive if the members stay fresh and energetic
than if they work overtime.
On-sire customer: Have a customer available on-site and full time.
Coding standards: Adopt common standards and conventions for naming, source
code fonnaning, documentation, and so on.
Undoubtedly, some of the practices are unique to XP, such as pair programming.
Extreme programming and RUP share a lot in common. In some sense, XP can be
viewed as a minimalistic form of the RUP. One of the key differences between the two
is that the RUP emphasizes building object-oriented models using modeling notations
defined in UML, while XP emphasizes producing executable code. However, building
object-oriented model using UML is often done in XP as welJ. Several commonly
used notations of UML will be discussed in Chapter 2. Unit testing and continued
integration will be discus ed in Chapter 6. Refactoring will be one of the main topics
of Chapter 7.
CHAPTER SUMMARY
Brook , F. P. (1987). "No Silver BuUet- E ence and Accident of Software Engi-
neering," IEEE Software 20(4).
Jacob on. I., G. Booch, and J. Rumbaugh ( 1999). The Unified Sofnvare Development
Process. Addi on-Wesle .
Kruchten, P. (2000). The Rational U11ified Proass. A11 Imrodu tio11, 2nd ed. Addison-
We ley.
EXERCISES
1.1 Search the Web or librarie to find out detail of 1.3 Search the Web or libraries to find out whether
some of the catastrophic failure of computer it is permissible to use software engineer as a
systems who e cause ha,·e be-en attributed to professional title without ce1tification in your
software failures. including the one mentioned country or state, and what the rationale is.
in this chapter.
1.2 Search the Web or librarie to find out details
of some failed oftware development projects
and the cause .
Object-Oriented Modeling
Using UML
CHAPTER OVERVIEW
In this chapter, we discuss the basic principles, concepts, and techniques of object-
oriented modeling. We introduce a number of commonly used notations in the Unified
Modeling Language (UML), including class diagrams, object diagrams, sequence dia-
grams, and use case diagrams. We conclude the chapter with a case study of object-
oriented analysis and modeling.
In thi ection, we di u, the ba i con ept and the prin iple , of object-oriented
development. \Ve also introduce some ~ imp le graphi al notations in the Unified
Modeling language (U IL) (Booch et al .. 1999] 1 for des ·ribing obje t-orientcd
models. We u e a sub ·et of U IL notations with minor adJptati ns in synt, x for the
ake of con, isten , ith Ja a.
l. Ul\ IL is a stand:.ml for objc~t-orienteJ moJ ling no1~11i 1ns cnJol'\ed by the Object I\ 1:magcmcnt Group
(0 •IG), nn indu ·trial ron ·t,rtium on obj~ct tc~hnologics.
19
-
20 ■ Object-Oriented Modeling Using UML
Each object has a unique identity. The identity of an object distinguishes the
object from all other objects. The state of an object is composed of a set of fields, or
arrribules. Each field bas a name, a type, and a value. The behavior of an object
is defined by a set of methods that may operate on the object. In other words, a
method may access or manipulate the state of the object. Methods are sometimes
called operations, and we consider these two tenns to be synonymous. Each method
al.so bas a name, a type, and a value. The type of a method consists of the return type
and the list of parameter types of the method. The return type can be void if the
method does not return a value. The value of a method is the implementation of the
method often expressed as a sequence of statements, in languages like Java or C++.
The features of an object refer to the combination of the state and the behavior of the
objecL
Two objects are equal if their states are equal, that is, if the values of the
corre ponding fields of the two objects are equal. Two objects are identical if they are
the ame object, that is, if they have the same identity.
The value of the field of an object are mutable. Those methods of an object
chat do not modify the slate of the object are called accessors, and those methods
of an object char could modify the state of the object are called mutators. A mutable
object is an object whose state may be modified by some of its methods. A mutable
object may have different states at different times. An immutable object is an object
2.1 Principles and Concepts • 21
whose state may never be modified by any of its methods, that is, an object that has
no mutators. The tate of an immutable object remains constant. Objects are usually
mutable. However, immutable objects are quite useful too.
A class defines a template for creating or instantiating its instances, that is,
objects. The terms object and instance are often interchangeable. The class from
which an object is created is referred to a the class of the object, and the object
is referred to as an instance of the clas . In mo t object-oriented languages, including
Java and C++, in tead of defining the feature of individual objects, the features of
objects are defined in the class that instantiate the object . Specifically, a class defines
(1 ) the names and types of all fi eld and (2) the name . type , and implementations of
all methods. The values of the fields are not defined or fixed in the class definition. The
values of the fields are mutable. Each instance of the clas · ha its own state. Different
instances of the class may have different state . The implementation of methods are
defined in the class definition and are therefore fixed for a given object. In other word ,
the values of method of an object are immutable.
Let's look at a simple cla Point that repre ent points in a two-dimensional
space. The Java code defining the class is shown on the right-hand ide.
The Point class defines two field : x and y, and one method: move O. The type
of both fields is i nt . The return type of move () i void and the Ii t of the p:irarneter
types of move() i (int, i nt ), since it take two parameter both of type int.
method,
The bon m comparunent contains the declaration. of the methods
... of the cla :
methodm
If, in ome context, the detail of the field - and method of the la s i. not important,
one may omit both the middle and the b tt m l'0mpa.rtments.
22 • Object-Oriented Modeling Using UML
The visibility, or accessibility, of fields and methods defines the scope in which
features of classe are accessible. Visibility can be one of the following:
The acces ibility of features will be discussed in more detail in Section 4.4.1. T he
Java and UML syntaxes for visibility are as follows:
public public +
protected protected #
package
private private
2. In 1his book. we use the following convention to defi ne syntax: the notation Foo (e g •n , 1 ) d t
. I bI . I bol h . f .., •1/ e eno es a
nonlcnmna ~ym o ; 1em11na sym s ~re~ own 111 bold ace Courier font (e.g., •). The entities between
square brackets ! J (e.g., (T)peJ) arc opllonal.
J. Packages arc discussed laler in this section fp. 25] and in Section 4.5 Ip. 134].
• I•
2.1 Principles and Concepts • 23
The multiplicity specification of a field specifies whether an object may have mul-
tiple occurrences of the field. The multiplicity specification is defined in Section 2.2.2
[p. 33].
Each parameter of a method can be specified using the Java syntax as follows:
Type Name
Name: Type
Field declarations
Date birthday (Java syntax)
birthday : Date (UML syntax)
Method declarations
void move ( int dx, i nt dy) (Java yntax)
-move (dx: i nt, dy : int) (UML ynta"<.)
The Point cla s shown earlier can be repre ented in UML as follm • at different
levels of detail.
Point
private int x
private int y
public void move(int dx, int dy)
-
24 ■ Object-Oriented Modeling Using UML
Point
-x:int
-y:int
+move(dx:int, dy:int)
Abbreviated fonns:
Point
X
y
move()
There are a number of variations for the contents of the top compartment:
• objectName
Omission of the colon and the class name denotes an object named obj e ctN ame
whose class is of no interest.
• : ClassName
Omission of the object name denotes an anonymous object of class ClassName,
which can be identified only through its relationship with other objects.
The fields and their values in the bottom compartment are described with the
foUowing syntax:
Field• Value
The bottom compartment may be omitted altogether if the attributes and values of an
object are of no interest
2.1 Principles and Concepts • 25
For example, instances of the Point class, with the states (0, 0) and (24, 40),
can be represented graphically as follows:
The Java code segment, on the right, shows the creation of the instances and the
assignment of the states.
Recipient pl
p1.move(10, 20) Method move()
Arguments (10, 20)
Packages
Classes are often grouped into a package. Package. an be organized into a hierarchy.
In other words, a package may contain cla e and -ubpackage . It i important
to point out that all clas e in the ame package mu t be clo ely related, since all
feature of a cla , except tho e that are private. are ac e ible to all cla e in the
ame pa kage. Detail. for u ing pa .kag ' in Java, ill be di cus ed in Section 4.5. l .
-
26 ■ Object-Oriented Modeling Using UML
(a)
2.1.2 Principles
Modularity
One of the fundamental principles of the object-oriented approach is the principle of
modularity. It is intended to control the complexity of large-scale systems through the
use of the divide-and-conquer technique.
Pri~ Modularity
A complex software system should be decomposed into a set of highly cohesive but
loo ely coupled modules.
■ each module is relatively small and simple (that is, hjghly cohesive); and
■ the interactions among modules are relatively simple (that is, loosely coupled),
ensuring that- by examining the module within, not without-each module will
be well-behaved and that, if all the modules are well-behaved, the entire system
also wiU be well-behaved.
Abstraction
In its purest sense, abstraction means separating the e ential from the none ential
characteristics of an entity. The result is a simpler but sufficiently accurate approx-
imation of the original entity, obtained by removing or ignoring the none enrial
characteristics. The abstraction principle in software development can be described
as follows:
Principle Abstraction
The behaviors, or functionalities, of a module bouJd be cbara terized in a uccinct
and precise description known as the co11tractual interface of the module. In other
words, the contractual interface capture the e en e of the behavior of the module.
The contractual interface is an ab traction of the module.
We can view a module a a senice provider and other module that u e the
services provided by the module as cliems of the module. \: e an view the contractual
interface as the service contract bet\ een the ervice pro idcr and it. clients.
service contract need only de cribe what e.rvi es ·ru1 be pro ided, not how the
service are to be provided. Therefore, de pite the fact that the rvice to be pro ided
are very complex, the , ervice ontract may be very imp le. ith a imple ervice
contract and an as uran .e by the . ervice provider of h noring the ontracl. the client
need only undectand the · imple contra t in order to u ·e the comp le , service .. The
contractual interfa e alto, s the client to u, e the . ervice , and not be oncemed with
the complexity of the er ice ·. In other , ord~. the comple. it of the module i hidden
within it.
Let us onsider the exrunpk of th telephone . The mechani m for providing
telephone . er ice i , a rather comp le. one. lt in olws routing and connecting calls,
28 • Object-Oriented Modeling Using UML
convening voice to electronic signals and back to voice, transmitting the signals. in
analog or digital mode, and possibly encrypting and decrypting the signals f~r secunt;
reason . However, telephone users (that is, the clients of a telephone service) don t
need to understand the mechanics of a phone system. All the users need to understand
i the manual that comes with the telephone set, which includes instructions on dialing,
speaking. and hanging up. The user's manual in this case is the contractual interface
of the telephone service, and it serves as an abstraction of the telephone service from
the user's perspective.
Encapsulation
A closely related and complementary principle is encapsulation, which stipulates that
the clients need know nothing more than the service contract while using the service.
Principle Encapsulation
The implementation of a module should be separated from its contractual interface
and hidden from the clients of the module.
Polymorphism
Several different service providers can honor the same contractual interface. More-
over, these service providers can be interchanged without affecting the clients. The
2.2 Modeling Relationships and Structures • 29
In this section, we introduce the UML class diagram for modeling the tatic tructures
of object-oriented software systems and various types of relation among the cJas es.
Class diagrams are the most common diagrams used in object-oriented modeling. A
class diagram consists of
■ a set of nodes that represent classes and interface ; and
■ a set of links that represent relationship among clas es.
2.2.1 Inheritance
4. The word poly111orphis111 ml!un · an i:nLity , ilh multiple~ m1 ·. In lhis particular context, it refer to a
conlructual inlcrfm:i: , ilh multiple interchangeable impkmcntntions.
--
■ The txtension relation between two interfaces. When interface 12 extends inter-
face l 1, interface 12 is known as a subinterface of interface l 1, and interface l 1
is known as a superinterface of interface 12.
■ The implementation relation between a class and an interfacq. When class C2
implements interface 11, class C2 is known as an implementation of interface
11, and interface 11 is known as an inte1face of class C2.
UML uses a different terminology for inheritance relationships. The extension relation
is also known as specialization, and the inverse relation is known as generalization
in UML. The implementation relation is also know as realization in UML.
Graphically, the inheritance relation is represented by a link from the subclass/
subinterface to the superclass/superinterface with a hollow triangle pointing toward
the superclass. The extension relation is represented by a solid link, and the imple-
mentation relation is represented by a dashed link, as shown in Figure 2.2. In class
diagrams, the regular class, field, and method names are shown in upright roman fonts,
as in MyClass. The names of interfaces and its methods are shown in italic fonts, as
in Mylnterface.
Conceptually, inheritance models the is-a(n) relationship in the real world; that is,
if C2 is a subclass/subinterface/implementation of Cl, then every instance of C2 is an
instance of Cl, and everything that applies to instances of Cl also applies to instances
of C2. The ex.tension relation between two classes is commonly associated with the
notion of reusing or sharing the implementation (that is, the fields and methods) of a
superclass by its subclasses. The extension relation between two interfaces represents
the expansion of the service contract. The implementation relation does not connote
reuse of implementations, but rather the implementation of a contractual interface by
a class.
As an example, let us consider the following set of classes that represent different
groups of srudents in a university. The class diagram is shown in Figure 2.3.
F"tgure 2.2
Superclass Superinterface Interface
6.
UML notation fOf'
lnhattana r~la-
tionships.
Subclass Subinterface Implementation
Figure 2.3
Student
Class diagram: in-
heritance relation
among classes rep- Nondegree Undergraduate Graduate
resenting student
groups.
Master PhD
Class Description
Levels of Abstraction
Cla. se and interface repre ent ab traction , and the inheritance relation hip orga-
nizes the cla ses and interfa e into different level of ab traction.
32 ■ Object-Oriented Modeling Using UML
1n other words, the superclasses represent more general abstractions and the
subclasses repre ent more specialized abstractions. Consider again the example of
tudents hown in Figure 2.3. The inheritance hierarchy shows different levels of ab-
straction of students in a university. The Student class represents the most general
ab traction of tudents, whereas its subclasses represent various specialized abstrac-
tions of tudents. The leaf classes (that is, classes with no subclasses) represent the
most pecialized ab tractions of students.
2. 2-2 Association
Figure 2.4
Class1I
- - - - ~ role
name
I Class2
role ~-----'
UML notation for
association rda-
tionship.
2.2 Mod~ing Relationships and Structures • 33
Figure 2.5
Student 1-•-_ _e_n_ro __
_ll_► -1
Course
Class diagram: as- advisee •
sociation relation-
ships.
• teach
1
1
Faculty
adviser
with Facult y and Student, respectively, in the association between Faculty and
Student . The role name may also have an optional vi ibility designator, that is +,
#, - ,or-.
The multiplicity specification is a comma-separated sequence of integer intervals.
An integer interval can be one of the following:
l . .u specifies a closed, that is, inclusive, range of integers from the lower bound
I to the upper bound u. Both the lower and upper bound are integer literals.
The upper bound may also be the asterisk character ( ), which indicates an
unlimited upper bound.
i specifies a singleton range that contains integer i, which is an integer literal.
* specifies the entire nonnegative integer range: 0. 1. 2. 3.. ..
0 . ·* 0 or more
1. · * 1 or more
2 .. 5 2 to 5
2, 5, 7 2, 5, and 7
1, 3, 5. ·* I, 3, and 5 or more
In Figure 2.5, the enroll a ociation i man -to-man ; that i a tudent may enroll
in any number of course . and a ourse ma have any number of tudent · enrolled
in it. The reach association i one-to-man ; that i ·. ea h ourse has onl one faculty
member to teach it, but a fa ult member ma tench an number of course . The
adviser-advisee a ·ociation L al o one--to-man ; that i , ea b . tudenl ha. one advi er,
but an advi er may ha e any number of ad i ee ·.
The graphical notation of an a iation ma al o indi ate the navigation of
the a o iation. If there i, a dire t or indirect refere nee from cl Cl to clas C2,
then it is navigabl from Cl to C2. n as , iation ma b nn igable in one or both
dire tions. By default, an a. s iation is a · ·urned to be navigable in both direction .
If nn a o iation is only navigable in one dire tion, it mu t be explicitly hown with
34 • Object-Oriented Modeling Using UML
a navioation arrow at the end of the association link pointing in the dired\on that
is navi;able. The navigation arrows should not be confused with the name direction
arrow. They are independent. ln the class diagram shown in Figure 2.5, the adviser-
ad-visee association is only navigable from an advisee to an adviser, not from an
adviser to advisees.
Flsure 2.6
Aggregate Aggregate
UML notations for role role
aggregation rda- name
tlonshlps.
name
role role
Component Component
aggregation composition
2.2 Modeling Relationships and Structures ■ 35
Figure 2.7 1 1
University - ----1* 1
College - - ---1· Department ~>-----l* Student
Class diagram:
1 1
aggregation rela-
tionship. chairman-of A A member-of
1 1.:
Faculty
to a university. The lifetime of a department may not exceed the lifetime of the college
it belongs to, and the lifetime of a college may not exceed the lifetime of the university
it belongs to. The part-of relationships between a university and its colleges and
departments are stronger than the part-of relationships between a department and its
students and faculty members. The former are considered composition relationships.
The distinction between aggregation and composition is entirely conceptual.
There is no distinction in how aggregation and composition relationships are im-
plemented. The design models in this book will not distinguish aggregation and
composition, and only the aggregation notation will be used.
2.2.4 Dependency
Dependency is a relationship between entities such that the proper operation of one
entity depends on the presence of the other entity, and change in one entity would
affect the other entity. A common form of dependency i the use relation among
classes. In other words, class C1 depends on class C2 if C1 u e C2 in place uch as
the parameters, local variables, or return types of its methods. The graphical notation
for dependency relationships is a dashed line with an arrow pointing in the direction
of dependency, as shown in Figure 2.8.
The class diagram shown in Figure 2.9 illu trates the dependency relationship.
The Registrar class has a number of method for adding and removing course in
the course schedules and enrolling and dropping tudent in cour e . The e methods
take parameters that are in tance of CourseSchedule, Course, and Student
classes. Therefore, the Registrar cla u e , that i , depend on, CourseSchedule,
Course, and Student cla e .
Figure 2.8
Class1 ~----------------1 Class2
UML notations for Class1 depends on Class2.
dependency rela-
tionships.
36 • Object-Oriented Modeling Using UML
Class dia!mlllls model the tructure of software systems in terms of static relationships
~
Figure 2.10
l Obj
1
: c\ an object
<<create>>
creating a new object
UML notations
0
I
Figure 2.11
I:PrinterQueue j
I
A sequence dia- I
add (this)
assi gnJobNo 0
-- - ------------------- --►
Figure 2.11 shows a sequence diagram that depicts the flow of execution when
a client issues a request to print a document. It indicates the following equence of
method invocations:
State diagrams depict the flo\ of control u ing the on ept of state and transitions.
State diagram, are generaJizati n. of the traditional finite rate machines (FSM).
A state i a ondition or siniation in the life of an obje t during which it ati fies
ome condjtion. performs ome a tion . or v rut' f r ome e ent . A tra11sitio11 is a
relation. hip between t\ o tate indi ating that an object in the fir t tale (the source
state) , ill perform certain a tion · and enter the econd state (the de tination state)
38 • Object-Oriented Modeling Using UML
when a specified event occurs and certain conditions are _sati~fied. The graphical
notation for the ba ic elements in state diagrams are shown m Figure 2.12. States are
drawn as round-cornered rectangles. Each stale has a name. Transitions are shown
as link from the ource tale to the target states. The labels of transitions are in the
following fonn:
l. lf there is a triggerless transition originating from the current state, the transition
is triggered and the object enters the destination state of the triggerless transition.
2_ Otherwise, the object waits for some events to occur. When an event occurs,
a. if there is a transition originating from the current state such that
(I ) the event that occurred matches one of the events specified in the label
of the transition, and
(2) all the guards specified in the label of the transition evaluate to true,
then the transition is triggered, the action specified in the label of the tran-
ition, if any, is performed, and the object enters the destination state of the
transition.
b. Otherwise, the object remains in the current state.
Figure 2.12
( State ) a state source state
UML notations for
s~diagram. events [guard] / action
• an initial state
a transition
@ a final state destination state
__....._______
2.3 Modeling Dynamic Behavior ■ 39
Each state may aJso have an entry action and an exit action, which can be specified
as follows:
entry / Action 1
exit / Action2
When an object enters a state, the entry action associated with the state, if any, is
performed. When the object leaves a state, the exit action associated with the state, if
any, is performed. I
Nested State Diagram In state diagrams, states can be nested, known as composite
states or superstates. The notations for the nested tales are shown in Figure 2.13. The
nested states, or substates, can be either sequential or concurrent. A composite state
with sequentiaJ substates contains a ne ted state diagram. When a tran ition enters
such a composite state, it enters the initial state of the ne ted tate diagram. At a given
moment, an object is in exactly one of the sub tates. For example. in Figure 2. 13(a), an
object can be in either state S11 or S12. A composite tate with concurrent ub tate is
used to model concurrency. Each concurrent substate contain a tate diagram. When
a transition enters such a composite state, it simuJtaneou ly enters the initial tates
of the state diagrams in each concurrent substate. At a given moment, an object is
simultaneously in one of the states of the nested tate diagram in each concurrent
substate. For example, in Figure 2.13(b), an object can imultaneou l be in one of
states S21 a or S21 b and one of states S22a or S22b. When a tran it:ion originate
from a composite state, it means that the transition may originate from an of the
states enclosed in the composite state.
Figure 2. 14 is a state diagram de cribing the operation of a cellular phone.
Operation starts in the initial state, which lead to the Off tate. The power-on event
triggers a transition that leads to the On tate. A the On tate i a ompo ite late.
the cellular phone enters the initial tate of the tate diagram n ted in the On tate
which leads to the Standby state. The tran ition labeled power-off originate from
Figure 2.13
so S1 S2
UML notations for S21
nested states in a S21a 1--~ S21b
state diagram.
S22
S22a 1-----'~ S22b
Figure 2.14
On
~---.. incoming call
A stat~ diagram: Standby r-----
operation of a power-<>n
cdular phoM. keypad-unlock keypad-lock
end
Active
0-91#1* Ringing
connection-fail
power-off
keypad-unlock
~ - -- ---------=:,,.I Talking
connection-succeed
Entry: LCDOn 0
Exit: LCDOf f ()
another composite Active state. Hence this transition may originate from any of the
states inside the Active state. Note that the Active state bas both entry and exit actions
associated with it. Thus, whenever the cellular phone enters the Active state, the
LCDOn () method (that is, the entry action) is invoked to tum on the LCD screen,
and whenever the cellular phone leaves the Active state, the LCDOff O method (that
is, the exit action) is invoked to turn off the LCD screen to conserve the battery. The
st.ate diagram nested inside the Active state depicts the flow of both making a call and
receiving a call To make a call requires the following sequence of events:
■ From the Standby state, unlock the pad (the keypad-unlock event) to enter the
Dialing state in Active.
■ In the Dialing state, dial the phone number by using the number keys (0- 9) or #
or *, and the cellular phone remains in the Dialing state.
■ From the Dialing state, push the t alk key (the talk event) to enter the Connecting
state.
■ From the Connecting state, if the connection succeeds (the connection-succeed
event), the cellular phone enters the Talking state. If the connection fails (the
connec1io11-fail event), the cellular phone returns to the Dialing state.
To receive a call requires the following sequence of events:
■ From the Standby state, an incoming call (the incoming-cal/ event) triggers a
tran ition that leads to the Ringing state in Active .
■ From the Ringing state, push the talk key (the wlk event) to enter the Ta lking
tate.
2.4 Modeling Requirements with Use Cases • 41
Note that the transition labeled end originates from the composite state Active. Hence
from any state in the Active state, pushing the end key (the end event) triggers this
transition, and the cellular phone enters the Dialing state. Similarly, from any state
in the Active state, locking the keypad (the keypad-lock event) triggers the transition
labeled keypad-lock, which leads to the Standby state.
Use cases and use case diagrams are used for modeling the requirements of the systems
to be developed and for deriving object-oriented models from the requirements.
Although use cases are not essential in object-oriented software development, they
provide important links between requirements and object-oriented models. Therefore,
use cases and use case diagrams play an important role in the RUP and UML.
Use cases describe the externally observable behavior of system functions, in the form
of interactions between the system to be developed and the external entities, known
as the actors, of the system. Each actor represents a role played by a set of external
entities that interact with the system. Actors may represent roles played by human
users of the system or by other systems. Use cases are intended to describe what the
system does, not how the system does it.
Each use case has a name and a set of scenarios. One of the scenario is the main
scenario. Optionally, a use case may have any number of alternative and exceptional
scenarios. Each scenario is a sequence of interactions between the actors and the
system. The main scenario describes the normal flow of events and the outcome of
the use case; that is, it describes what will happen when all the precondition are met.
Sometimes, there are a number of alternative flow of events and outcome that are
also considered to be normal. The e alternative flows of events can be described as
alternative scenarios that complement the main scenario. The exceptional scenarios
describe the flows of events and the outcome when certain preconditions of the
normal scenarios are not met, or when errors or exceptions occur during the normal
flow of events.
Scenarios of use cases are usually de cribed informally. Following are some of
the commonly used fonnats for de cribing a scenario:
■ use cases,
■ actors. and
■ relationships among actors and use cases.
The graphical notation for use cases and actors is shown in Figure 2.15. Several types
of relation hips can be represented in use case diagrams:
■ Ct includes c2 means that use case c2 is part of use case ci, and
■ c2 extends c I means that use case c2 is a special case of use case c 1•
Agure 2.15
User ~ an actor
UML notations for
actors and UK
caKS.
8 a use case
-
42 • Object-Oriented Modeling Using UML
• u e cases,
• actors, and
• relationships among actors and use cases.
The graphical notation for use cases and actors is shown in Figure 2.15. Several types
of relationships can be represented in use case diagrams:
• Ct includes c2 means that use case c2 is part of use case Ct, and
■ c2 extends Ct means that use case c2 is a special case of use case Ct.
Figure 2.15
8 a use case
2 .4 Modeling Requirements with Use Cases • 43
Figure 2.16
Extension relation-
ships among ac-
tors.
Let us consider the use case diagram shown in Figure 2.17. Association relation-
ships between the actors and the use cases indicate that
■ a student may participate in use case Check grades.
■ a faculty member may participate in use cases Get Roster and Enter Grades.
■ an administrator may participate in use case Verify grades.
■ any user may participate in use case Validate User.
Furthermore, the include relationship indicates that use case Validate User is part of
all the other use cases shown in the diagram. The extension relationship indicates that
use case Enter Grades is a special case of use case Get Roster, since entering grades
requires getting the roster with an editable grade field.
Figure 2.17
, ,, I
I
, I
,' <<i nclude>>
I
Faculty Enter I
I
grades I
I
Verify
grades
Administrator
• Object-Oriented Modeling Using UML
■ use case ,
■ actors, and
■ relationships among actors and use cases.
The graphical notation for use cases and actors is shown in Figure 2.15. Several types
of relationships can be represented in use case diagrams:
■ c 1 includes c2 means that use case c2 is part of use case Ct, and
■ c2 extends c, means that use case c2 is a special case of use case Ct.
Figure 2.15
8 a use case
2.4 Modeling Requirements with Use Cases • 43
Figure 2.16
+
Student Faculty Administrator
Let us consider the use case diagram shown in Figure 2.17. Association relation-
ships between the actors and the use cases indicate that
• a student may participate in use case Check grades.
• a faculty member may participate in use cases Get Roster and Enter Grades.
• an administrator may participate in use case Verify grades.
• any user may participate in use case Validate User.
Furthermore, the include relationship indicates that use ca e Validate User i part of
all the other use cases shown in the diagram. The extension relation hip indicate that
use case Enter Grades is a special case of use case Get Roster, since entering grades
requires getting the roster with an editable grade field.
Figure 2.17
' '' I
,' <<include>>
I
Faculty Enter I
I
I
I
Verify
grades
Administrator
• Object-Oriented Modeling Using UML
2.5.1 Conceptualization
Figure 2.18
Customer
System Inventory
administrator manager
L----------------~
e-bookstore. Although we decide to focus on the customer side of the sy tern first, it is
also important to identify most of the important actors and use cases in the y tern early
on. The elaboration and implementation of these u e case can be deferred to later
iterations. The main actor for the management side is identified a Manager. Ho, ever,
it also appears that there are two categories of distinct management tasks: one deal
with the management of the catalog of the merchandi e offered by the e-bookstore,
and the other deals with the management of the inventorie and the proce ing of
the orders. Therefore, we further identify two extended actors of Manager: Catalog
Manager and Inventory Manager, which are as ociated with u e cases corre ponding
to their main tasks: Manage Catalog and Process Order. respectively. A common
function for both customers and managers i to Jog on to the y tem. So it i reasonable
to define Logon as a separate use case that i incJuded b other u e c e involving
customers or managers. The logon function al o leads u to n function that i not
explicitly specified but implied in the requirement , that i . the managemenr of the
accounts for both the customers and managers. Therefore, a ne, u. e ca e Manage
Account i identified. Since the ta ks of managing the a ount are di tin t from aJl
the other ta ks, it i also rea onable to introduce a ne\ a tor, System Administrator,
to be a ociated with the ne, u e ca~e.
The use ca e Shop will be the focu of the early iteration. U. e cru e Shop i
de cribed in Figure 2. t9. A u ·e case u. uaU ha. a pre ondition, which pecifies the
condition under whi h the u e ca i appli able. The main enari of use ca e Shop
i described u ing a two- olumn table, in\ hi h th " left-hand column describes the
input events generated b a tors and the right-hand olumn de cribc. the re ponse
produc d by the ' ystem.
-----============================:=====~;;;;.;;;::=~~ ..
46 • Object-Oriented Modeling Using UML
Alternative scenario:
The customer saves the shopping cart without checking out.
Exceptional scenario:
Customer authentication fails; repeat the logon procedure.
Exceptional scenario:
Payment validation fails~prompt the customer for new payment information.
tdentifylng Classes
The first step in building an object model of the system is to identify the classes
inv_olved !n the use ca~es. This step _is perhaps one of the more perplexing tasks in
obJect-onented modeling. The questions arc What can /Je a class? and What f eatures
(that is, fields and methods) should a class have?
2.5 Case Study: An E-Bookstore • 41
The answers largely depend on the domain and the problem to be solved. The
following are some simple guidelines. Classes represent entities, not actions, and can
represent many different types of entities, including
• physical objects, such as equipment, devices, and products;
• people, such as students, faculty, and customers, and the roles they play;
• organizations, such as universities, companies, and departments;
• pl~ces, such as buildings, rooms, and seats;
• events, such as mouse clicks, service requests, and purchase orders; and
• concepts, such as multidimensional spaces, transactions, and weather maps.
Actions should be modeled as the methods of classes. A simple rule is that class names
should be noun phrases and that method names should be verb phrases.
Only the classes and features of classes that are relevant to the problem to be
solved need be included in the model. One of the most straightforward methods of
identifying classes is the following:
• Underline the verbs and nouns in the description of the requirements and use
cases.
• The nouns are the candidates for classes or attributes, and the verbs are the
candidates for methods.
By identifying the nouns in the core requirements of thee-bookstore, we can at
least identify the foUowing classes:
Class Description
Class Description
Customer
name
customerlD
password
shippingAddress
billingAddress
The list of fields may still be incomplete, but it is a start. As we go through more
iterations, additional fields that belong to the Customer class should become clear.
At this stage, it isn' t necessary to completely specify the type or visibility of each
field. The key issue here is what fields a class should have.
Toe fields shippingAddress and billingAddress in Customer represent
addresses. This observation naturally leads to a new class Address. The shipping-
Address and billingAddress fields are both instances of the Address class.
To identify the fields of the Address class, we simply rely on common sense and
knowledge of actual forms of addresses.
Address
street
city
state
country
postalCode
Book
MusicCD
title Software
author title
publisher artist title
yearPublished publisher publisher
edition yearPublished yearPublished
volume volume version
ISBN ISBN ISBN
price price price
Obviously, some fields are common to all three classes. The commonalities are
not coincidental because when calculating the total price of a mixed order of books,
CDs, and software, each item should be treated the same way. The three classes then
-
should be subclasses of a common superclass, and the common fields of the three
classes should belong to their common superclass. Thus, another way of identifying
classes is by identifying the commonalities among existing classes and extracting
the commonalities to a common superclass. This process is known as generalization.
The following are the revised classes after extracting the common fields to the Item
superclass:
Item
title
publisher
yearPublished
ISBN
price
'~
I I
Book MusicCD Software
contains the item and the quantity ordered. An instance of ShoppingCart contains
a set of instances of Order Item. \ 1
1 '
•
ShoppingCart .. A
[V Orderltem
*
1
Order \ C~stomer \
Customer customer
1 1
ShoppingCart items \ ShoppingCart \
float salesTax
float shippingFee
1
float total
Payment payment
* \ ,Payment I
I
Now, let us consider the main class EBookstore, which represents the entire
system. Obviously, the system has to keep records of a variety of things, including
all the items on sale, all the customers, and all the orders that have been placed. The
relationships among the classes identified so far are shown in the following diagram.
EBookstore Customer
1 1
Order
Item
• 1
Orderltem ShoppingCart
This step completes the initial version of the object model of thee-bookstore. An
initial object model captures the most important and obvious aspects of the system
but is often incomplete. Several iterations are usually necessary to derive a complete
object model. A useful technique for deriving complete analysis models is through
the analysis of responsibilities of classes and collaboration among classes. The idea is
to enumerate and analyze all the use case scenarios, assign responsibilities to classes,
and identify the collaborators of each class. This usually leads to identifying new
responsibilities for existing classes or identifying new classes. The process continues
until all the responsibilities have been assigned and all the collaborators have been
identified.
In the e-bookstore example, additional responsibilities and classes can be identi-
fied by analyzing additional use cases and scenarios, such as new customer registra-
tion, and by elaborating the requirements for inventory management. order process-
ing, and so on. The perivation of a more complete object model of the e-bookstore is
left as an exercise.
,,
■ Use case and u e case diagrams are used for modeling the requirements of
the system to be developed and•for deriving object-oriented models from the
requirement . They provide important links between requirements and object-
oriented model . U e cases describe the externally observable behavior of a
system function, in the fonn of interactions between the system to be developed
and the external entities, known as the actors of the system. Use cases are intended
to de cribe what the ystem does, not how the system does it.
FUITHII IIADIMeS
Booch, G., J. Rumbaugh, and I. Jacobson (1999). The Unified Modeling Language
User Guide. Addison-Wesley.
Fowler M., and K. Scon (1997). UML Distilled: Applying the Standard Object
Modelling Language. Addison-Wesley.
Larman, C. (2002). Applying UML and Patterns, 2nd ed. Prentice Hall.
OMG (2001). OMG-Unified Modeling Language, vol. 4, http:/ /w.TW. omg. org
/uml
Rumbaugh, J., l Jacobson, and G. Booch (1999). The Unified Modeling Language
Reference Manual. Addison-Wesley.
IXIICIIII
2.1 Based on your experience of using bank ATM a. Determine the actors and use cases of the sys-
machine . precisely model the behavior of an tem, and describe the relationships among them
ATM machine u ing tate diagrams. The stale using use case diagrams.
diagrams bould de cribe at least the following b. Elaborate each of the use cases. Describe the
scenario : scenarios in each of the use cases.
• Cash depo it c. Identify the classes of the system, and describe
• Cash withdrawal the responsibilities and the features of each
• Checking account balance class.
d. Determine the relationships among the classes,
and u e class diagrams to depict these relation-
2.2 Based on your experience of using portable ships.
mu ic CD player , preci ely model the behavior
of a portable mu ic CD player u ing state
2.3 An airline ticket reservation ystem. The
diagram .
sy tem should allow a customer to specify
Develop an object-oriented model for each of the the origin and destination of travel, preference
following y tetru de cribed in E.xerci cs 2.3- 2.5. on the departing and returning dates, the
,·
CHAPTER 3
Introduction to Java
CHAPTER OVERVIEW
In this chapter, we discuss the key cha@cteristics of Java and examine its run-time archi-
tecture. We use two very simple programs, an application and an applet, to illust@te the
basic structure of Java programs and use of the Java development toolsand environment.
55
56 •
---
Introduction to Java
The fir t official release of Java, JDK 1.0, took place in January 1996. Since then,
there have been a number of major releases of Java, each of which offered significant
enhancements and improvements to the preceding release. JDK 1. 1 was released in
February 1997. It introduced many significant changes from JD K 1.0, some of which
are not backward compatible with JDK 1.0. Today, JDK 1.0 is obsolete, while JDK
1. 1 remain at the core of the subsequent releases and is compatible with subsequent
re lea e . A number of key technologjes were introduced in JDK 1.1, including security
and authentication, object serialization, remote method invocation (RMI), and Java
bean s. These technologies have become the foundation of many other technologies
introduced in later relea es. In many ways, JDK 1.1 was the coming of age for Java.
One of the most significant milestones of Java was the release of the Java 2 Platform,
that is, JDK 1.2, in December 1998 (Figure 3 .1). The Java 2 Platform introduced
three separate edit.ion of Java: Standard Edition (12SE), Enterprise Edition (12EE),
and Micro Edition (12ME). 1 Each Java platform edition defi nes a set of tec hnologies
I. J2EE and J2ME were announced in December 1998 but were not released until June 1999 at the Ja va011 e
Developer Co 11f erence .
3.1 An Overview of the Java 2 Platfonn ■ 57
--
Java 2 Micro Edition (J2ME)
that aim at a particular category of computing devices: Java virtuaJ machine that fit
the type of computing device, libraries and Application Program Interface (APls)
specialized for the type of computing device, and tools for deployment and device
configuration.
Java 2 Platform Standard Edition The Java 2 Platform Standard Edition (J2SE) is
for developing application that run on de ktop computer . Such computer usually
have reasonably powerful proce sor with an adequate amount of memory and disk
space and serve a single user.
J2SE include the following component :
Java 2 Platform Micro Edition Java 2 Platform Micro Edition (J2ME) is for de-
veloping applications that run on small, resource-constrained devices, such as palm-
top computers, cellular phones, and television set-top boxes. Compared to desktop
computers, such devices usually have much less powerful processors, very limited
memory pace, and small screens.
J2ME defines two configurations to target two broad categories of devices:
Each configuration also define a number of profiles to addre s the special need
of pecific families of device . One of the profiles in CDC is the Personal Profile, al o
known a Per onal Java, which upports a subset of the clas librarie upported by
J2SE. One of the profiles in CLDC is the Mobile Information Device Profile (MIDP)
for cellul ar phones.
A number of Java virtual machines are available on different types of device
including the KYM , the K-Virtual Machine, which runs on a number of PDAs and
cell phones.
3.2 The Java Run-Time Architecture ■ 59
Platforms
The Internet is a heterogeneous, open, and self-organizing anarchy. It consists of a
vast number of computers with different CPUs running different operating systems.
The combination of the CPU and operating system of a computer is referred to as the
platform of the computer,2 which defines the key characteristics of the operating envi-
ronment of that machine. Popular platforms include the Sun Spare CPU with the Sun
Solaris operating system (Sparc/Solaris); the Intel Pentium CPU with Linux operating
system (Intel/Linux); the Intel Pentium CPU with the Microsoft Windows operating
system (Intel/Windows); and the PowerPC CPU with the MacIntosh operatjng system
(PowerPC/MacOS).
Programs usualJy are platform specific; that is, a program developed and com-
piled for one platform will not automaticalJy run on a dilfereot platform. unJe s the
two platforms are compatible or one platform can emulate the other.3 Mo t of the time,
when a program needs to run on different platforms, special version of the program
must be developed and compiled for each environment. Converting a program to run
on a different platform is called porting. In general, porting i a nontrivial task. One
of Java's advantages is that it is platform independent; Java programs are designed to
run on different platforms without porting.
Security
Owing to the open nature of the Internet, computers connected to the Internet are all
susceptible to attack from malicious program in a\ ide variety of form . An attacker
who manage to break into a computer could acce or modify data. eave drop on
messages includjng e-mail and online tran action u ing credit card , or even cau e
irrecoverable damage to the oftware and hardware on the computer.
A completely ecure but open environment i unattainable. However, some effec-
tive methods in network technology and cryptography ha e been developed to reduce
sigruficantly the ri ·k of ecurit breache . The Java run-time environment is de igned
to prevent potential ecurity breaches while nmning Java programs on both the client
and erver machines.
2. omctimcs. the phrases /1e1n/m1rt' ple1tfon11 and svfrwart! p/11tjc>m1 :.ire usc<l to refer to the CPU and the
opcr;lling s stem of the rnmputcr, I'\' ·pcctively.
3. There arc II number of DO /Winu0, · emulators for MacO and olaris.
60 • Introduction to Java
There are two conventional models for executing programs: compilation and inter-
pretation.
Compilation
In compilation, a compiler tran lates, or compiles, the source code of a program into
machine code, allowing the program to be executed directly by the operating systems
and the hardware. The compilation approach is invariably used in programming
languages intended for developing large-scale, efficient, and reliable systems, such as
C, C++, Ada, and Eiffel. The compilation approach has several important advantages:
■ Modem compilers perform extensive static analysis, such as strong type checking
and datafiow analysis, to detect many potential errors in programs at compile
time.
■ Modem compilers apply sophisticated optimization techniques, such as common
expression extraction and loop strength reduction, before generating machine
code. Optimization produces efficient code and eliminates the need for hand
optimization, so programmers can focus on the functionality and maintainability
of the programs, not their efficiency.
■ Executing machine code generated by compilers is far more efficient than inter-
preting source code directly.
■ For commercial software vendors, software can be delivered to customers in
binary executable form, thus protecting the source code as a trade secret.
The main deficiency of the compilation approach is that the executables are
platform dependent. Two options are available for allowing a program to run on
different platforms, but neither one is appealing. The first option is to port and compile
the ource code for each platform. However, porting source code to different platforms
is tedious, time-consuming, and error-prone. In addition, the number of different
platforms is potentially large, and porting to all of them may not be economically
feasible. This approach is adopted for the widely available Netscape browser, which
is mostly written in C and ported to every major platform.
The econd option is to provide source code and porting instructions to allow
customers to port programs themselves to the platforms they use. This approach shifts
the burden to the customers and causes the original developer to lose control over
software quality and compatibility. This approach also exposes the source code to
outsiders. However, it is often adopted for noncommercial or open-source software
products, such as the Linux operating system. There are large-scale and industrial-
strength software systems that have been ported to different platforms in this fa hion.
Interpretation
In interpretation, an interpreter directly par es and executes the ource code of a
program without generating machine code. The main advant age of the interpreta-
tion approach is it quick turnaround of edit- run cycles while developi ng the code.
3.2 The Java Run-Time Architecture ■ 61
Figure 3.2
Java source code
Execution of Java
programs.
Java compiler platform
independent
Java machine
CPU1 native
machine code
platform
Java virtual machine dependent
- - - - - ' - - -- I
CPU2 :
L----=--_J I
- - --- - -- -- -- - -'
Java virtual machine
Thus the interpretation approach is often adopted for programming language used
for prototyping, or rapid application developmem (RAD), uch a BASIC, LISP, and
Smalltalk.4 This technique requires no compilation, and error can be di covered only
at run time. The interpretation approach i platform independent becau e programs ex-
ist only in their source form. However, oftware product developed with interpretive
languages must be delivered a ource code. The main di advantage of the interpre-
tation approach are the lo of tatic analy i and code optimization and the much
slower execution speeds than those generally achieved with compiled executable .
4. omc of these languages ul ·o ha e l'Ompiler ·. fore. ample, licrosoft Visual BASIC includes both an
interpreter an<l a compiler.
62 • Introduction to Java
The Java execution model essentially maintains all the advantages of the compila-
tion approach and accomplishes platform independence. The execution of byte-code
is far more efficient than the direct interpretation of source code, but it still is not as ef-
ficient as the direct execution of native machine code.6 With further improvements in
byte-code optimization and compilation, as well as improvements in the JVM, the ef-
ficiency of Java will approach that of compilation languages targeting native machine
code, such as C++.
Java Byte-Code
Java byte-code refers to the instructions of the Java virtual machine. A JVM instruc-
tion consists of a I-byte opcode, short for operation code (hence the name byte-code),
and zero or more operands, which are the parameters of the instruction. Operands
5. Sometimes, we si mply use lhe lcnn Java vinual machine Lo refer to the interpreter of lhe Java virtual
machine.
6. J?va ~asn' t lhe fi~l to_be based on this approach. P-code for PASCAL developed al the Un iversity of
Callfom1u _ut San Diego in the lale 1970s was lhe first platform-independent, or porwble, intermediate
code for high-level languages. A few other languages. uch as Emacs LISP, also compi le source code to
byte-code.
3.2 TM Java Run-Tlme Architecture • 63
vary in length, and the number and lengths of the operands are determined by the
opcode.
opcode (1-byte)
operand1
operand2
The core of the NM involves a loop that can be described roughly as follows :
do {
fetch the opcode byte of the current instruction;
fetch the operands, depending on the value of the opcode byte;
execute the instruction;
} while ( notdone );
■ The pc (program counter) register contains the address of the next instruction to
be executed.
■ The opt op register points to the top of the operand stack.
■ The vars register points to a set of local variables of the current method.
■ The frame register points to the execution environment structure.
The JVM also uses a garbage-collected heap to store aJJ objects at run time.
The JVM instruction talce operands from the operand stack, operate on them, and
return the results to the stack. The operand tack is 32 bits wide. The JVM in tructions
perform the following functions:
■ Stack manipulation
■ Array management
■ Arithmetic and logical operations
■ Method invocation and return
■ Exception handling
■ Synchronization of multiple thread ,
A detailed pec ification of the JVM and the format of the . class file are
presented in Lindholm and Yell in [ 1996].
A Java application must be explicitly invoked, like any other program, by issuing
a command on the command line or electing a command from a menu. Therefore,
an app can be invoked only by an authorized u er of a host, unle s the ecurity of
the host has already been compromised. Java imposes no restrictions on the behavior
of apps and their access to the host environment. Presumably, authorized users are
responsible for the consequences of executing applications.
Applet are embedded in Web pages and are invoked automatically when the
Web page containing the applets are loaded by Java-enabled browsers. Most of the
time, a user is unaware of the contents and behavior of the applets embedded in a Web
page before it i loaded in a browser. Thus, ensuring that executing applet will not
compromise the security of the host on which the browser and the applet are running
is imperative.
Security of JVM
Java virtual machine provides several layers of defense against potential security
attacks through applets.
Shielded memo,y addresses: The Java language does not allow direct manip-
ulation of memory addresses. There is no way for someone to access a
specific memory cell or forge a pointer to an address. Memory allocation
and deallocation are handled automatically and transparently by the JVM at
run time.
Verification of byte-code: The JVM enforces verification of the byte-code before
it is executed. Verification involves loobng for any improper structures and
control flows in the byte-code, any violation of access restrictions, and any
violation of the type system. The purpose of the verification is to ensure that
the byte-code is the output of a legitimate Java compiler, is generated from
a consistent version of the source code, and has not been tampered with.
Run-time security manager: While executing an applet, the JVM consults a
security manager whenever a potentially insecure operation is about to be
performed. The security manager decides whether to allow the operation.
The security manager is customizable to allow implementation of different
security policies. In fact, browsers from different vendors implement slightly
different security policies.
The security policies enforced by the security manager are configurable. Com-
monly implemented security policies include the fo11owing restriction on applet :
• An applet usually is not allowed to read or write files on the ho t that i executing
it. Some browsers allow certain exceptions.
• An applet is not allowed to communicate with hosts other than the one that it
comes from .
• An applet i not alJowed to start other programs, execu te operating sy ·te m
commands, or inquire about certain ystcm propertie of the host.
3.3 Getting Started with Java ■ 65
These Java security measures form the so-called sandbox, within which all
applets are confined. McGraw and Felton [1997] discuss the security issues involvin
Java applets in detail. Schneier [1996] gives an excellent general introduction t~
cryptography and computer security.
In this section, we present two simple Java programs-an app and an applet- to
illustrate the basic structures of Java programs and the fundamentals of compiling
and running Java programs.
As the Internet has reached almost everywhere on Earth, the next logical step would
be an interplanetary Internet. NASA is planning to have the first Internet host beyond
planet Earth online early in the twenty-first century. Imagine bow exciting it would
be to receive a greeting from a close neighbor-Venus.
PURPOSE
To illustrate the basic structure of Java programs.
DESCRIPTION
Thi application displays a greeting message: Hello from Venus! The me age is in
plain text and is written to the standard output (that i , the command con ole from
which the application is invoked).
SOLUTION
Assuming that you have in tailed the Java Development Kit on your y tern, here are
the teps to build and run the Java application.
1. Type in and save the following Java ource code in a file named Hello• java.
2. Compile the Java source code, using the Java compiler j avac: 7
Note that the argument of j ava is the class name, not the file name, that is,
without the extension . j ava or . class.
In this case, class Hello has only one method-the main () method. (See
Section 4.4.5 [p. 113] for discussion of the main O method.)
The main () method of the Hello class simply prints out the following message
to the console, or the standard output:
System. out refers to the standard output. The println () method prints out its
argument as a string with a new-line character appended to the end.
Source Files
Java programs are stored in files . Unlike many other languages, Java enforces certain
rules about how classes should be placed in files and how files should be named. A
simplified version of the rules is as follows:
7. Throughout Lhis book, venus¼ is used Lo stand for the command prompt in a console or shell window.
3.3 Getting Started with Java • 67
■ Usually, each file should contain a single class. However, Java allows a single
file to contain multiple classes, but with certain restrictions. (See Sections 4.5.1
and 6.1 [p. 134] for details.)
■ The file name should match the class name, including the capitalization. Java is
case sensitive.
These rules make it easy to locate the source code of a class by simply listing or
searching the file names.
Comments
There are three types of comments allowed in Java programs:
Java applications must be invoked from command con oles. Java applets can be
embedded in Web pages. They are downloaded via the World Wide Web and invoked
by Java-enabled browsers.
PURPOSE
To illustrate the basic structure of Java applet and the ba ic graphics capability of
displaying text and choosing font, size, tyle, and color.
DESCRIPTION
This applet display. the greeting me sage Hello from Venus! graphically. It consists
of a text message and an image of the planet Venu .
68 • Introduction to Java
SOLUTION
Here are the steps to build and run the Java applet:
1. Type in and save the following Java source code in a file named
HelloFromVenus.java.
Hello from Venus! ap let: BelloFromVenus. ja.ve:
import java.awt.•; -"')~(CA.Ql\\(S _ '" b o,-:}--
V11port ⇒ ~va.applet . Applet; --=) nlC\-ycQ_~ ~ v ~'
public class HelloFromVenus extends Apple {
public void paint (Graphics g) { ,
Dimension d = getSize ( ) ; comr'YKtlVc.Q:l-e. u) ¥) fG~
r L
.' 6 ~e:
~
g.s~tColor(Color.b~ack); . / ~\-r::e.-i\'Y'("f\\
g.fillRect(O,O,d .width,d.height); .~ ·
g . setFont(new Font("Sans-serif", Font.BOLD, 24));
g. setColor (new Color (255, 215, 0)) ; // gold color
g . drawString("Hello from Venus!", 40, 25);
_g . draw Image (get Image (getCodeBase ( ) , "Venus. gif") ,
~J l'nS ~¾ -\-0 :::C(-e,a) 20, 60, this);
}
}
2. Compile the Java source, using the Java compiler to generate the byte-code file
HelloFromVenus.class.
venusi. javac HelloFromVenus.java
3. Type in and save the following HTML source in a file named HelloDemo. html.
We may name the HTML file anything we want to.
Be sure that the HTML file, the Java source file , and the byte-code file are in the
same directory. The image of planet Venus is stored in a separate file named
Venus. gif. Be sure to place the image file in the same directory with the other
files.
3.3 Getting Started with Java ■ 69
Figure 3.3
'
'Applet started.
4. One way to view the applet is to use the applet viewer in JDK:
Figure 3.4 X
(0,0)
j ~
Viewing area of
applets and the
Java coordinate
system.
y height
I I
- --
Width
usually a browser. An applet must interact with its applet context according to a set
of prescribed conventions (that is, contracts).
The contract regarding the paint () method of an applet includes the following: 8
■ Each applet is assigned a rectangular region on the Web page in which it i
embedded. The dimension of this rectangular region is set in the <applet> tag
in the HTML file.
■ An applet implements the paint () method to paint the appearance of the applet
in the rectangular region. The origin (0, 0) is located at the upper left corner of
the rectangular region, as shown in Figure 3.4.
• The paint () method is invoked by the applet context whenever the applet is
active and the rectangular region designated to the applet becomes visible. This
statement implies that the paint () method will be invoked when the applet is
initially loaded in a browser.
Later, we discuss in detail most of the language features and classes involved in
this applet. A cursory understanding of what is going on in the paint () method of
the HelloFromVenus applet is sufficient at this point. Line by line, each statement
in the paint () method
1. gets the dimen ion of the rectangular region designated to the applet;
2. sets the pen color to black;
3. fills the entire rectangular region with black color, which paints the background;
4. ets the font to ans serif, boldface style, and 24-point size;
5. set the pen color to gold;
6. draw the text me age at the top in gold color; and
7. draw the image of the planet Venus below the mes age. The image i tored in
a file named Venus. gif .
~- T~e paint O m~lhod is declared in the Component eta s, which is a superclass o f Applet . Sec
ccuon 5.5 for deLa1ls about the paint () method.
3.3 Getting Started with Java • 71
Attribute Description
code Specify the name of the byte-code file containing the applet
width Specify the width of the applet in pixel
height Specify the height of the applet in pixels
9. Rnggctt cl al. (_000) give · n rnmpklc and definitive de cription of HTML 4.0, the current tandard.
The most up-lo-dnlc info rmation about HTML can be obtnincd from the w -' Consortium al 11•11•w. wJ .org.
72 • Introduction to Java
which creates a Java archive (JAR) file named Hello.jar .10 In order to use the JAR
file, we must specify the JAR file name in the <applet> tag, using the archive
attribute:
<applet code="HelloFromVenus.class
archive•"Hello . jar"
width=300 height=350>
</applet>
A browser that supports the archive feature will download the JAR file instead
of the class file. The class and image files will be extracted from the JAR file.
You are unable to invoke JDK tools such Be sure that the PATH environment variable
as javac, java, or appletviewer. includes the JDK bin directory.
You get deprecation messages while The programs contain features that are
compiling programs. deprecated. The programs were developed
for an older version of Java compiler than
the one you are using. As of version 1.4,
deprecation is only a warning, so programs
using deprecated features should still run
without problems. However, you should
remove deprecated features from programs
because they may not be supported in future
JDK releases.
Your applets run fine when you use the Be sure that the version of JVM in your
applet viewer but do not run in a browser. browser is equal to or higher than the version
of JDK you used to compile your programs.
When transferring files to a Web host via
FfP, be sure that you transfer the class
files in binary mode and that the file names
are intact, not truncated or converted to all
uppercase.
10. Use of command-line utility jar is similar to use of the UNIX tar utility. Its syntax is
jar [option s] destination input-files
See JDK tools documentation for details.
Further Readings
• 73
CHAPTER SUMMARY
• The primary design goals of the Java run-time architecture are platform indepen-
dence, efficiency, and security.
• The JVM is an abstract computing machine that execute Java byte-code. It can
be implemented as interpreters, just-in-time (ITT) compilers, or Java chip -
hardware implementations of the JVM. The in truction of the Java virtual ma-
chine are called byte-code. The Java compiler compiles Java source code to Java
byte-code and stores the resulting code in files with the extension . class.
• The two common types of Java programs are application and applets. Applica-
tions (apps) are full-fledged Java program with full acce to y tern resources.
Applets are programs that are embedded in Web page with re tricted acce to
system resources to prevent break-ins to the ho t that run the applet .
FURTHER READINGS
Arnold, K., and J. Gosling (2001). The Java Programming language, 3rd ed.
Addison-We ley.
Ka em, N., and Enterpri e Team (2000). Designing Enre,prise Applicarions wirh
Java 2 Platform, Emerprise Edirion. Addi on-\Ve ley.
Lindholm, T., and F. Yellin ( I996). Th e Ja\'{I Virwal Machine. Addi on-Wesley.
d
McGraw, G., and E. W. Felten ( 1997). Jal'(l ecuriry- Hosrile App/ers, Holes, all
A111idotes. John \Viley & on. .
Riggd, R., A. Taivalsaari and M. Vandenbrink (200 I). Programming Wireless De-
vices with the Ja\'(l 2 Platform, Micro Edition. Addi on-Wesley.
74 • Introduction to Java
EXERCISES
3.1 Write a Java application similar to the 3.2 Develop an applet similar to the HelloFrom-
Hello . Java application in Example 3.1. This Venus applet in Example 3.2. Your new applet
new program should contain a class named should display an image of your choice. Change
MyFavori tePoem. It should call System . out the color, size, and textual display of the image
. println () to display a poem that you like. as necessary.
(Hint: Look ahead to the next chapter for infor-
mation on the Java printing features that can be
used to display this poem neatly and elegantly.)
Elements of Java
CHAPTER OVERVIEW
In this chapter, we discuss the basic elements of Java, including operators and expres-
sions, data types, garbage collection, control structures, class decla@tion, pa@meter
passing, packages, and exceptions. We also discuss some of the more commonly used
classes in the Java class library. We present several simple programs to demonstrate string
manipulation and basic input and output ope@tions. Finally, we develop a simple ani-
mation applet.
75
76 • Elements of Java
and sound justifications but are often subject to misuse and bad programming prac-
tices, partly because of backward compatibility with C. In contrast, Java is designed
to be ( 1) more selective and restrictive-to prevent bad programming practices, even
at the cost of sacrificing expressiveness and convenience in some cases; (2) easy to
use, and versatile; and (3) suitable for a variety of tasks, including object-oriented
programming, numeric computation, and system programming.
Java differs from other object-oriented programming languages in several impor-
tant respects:
■ Unlike C++, Java is purely object-oriented. All nonstatic methods in Java are
polymorphic (i.e., virtual in C++ terminology). It does not allow global variables
and stand-alone functions, as C++ does.
■ Unlike Smalltalk, in which everything is an object, including values of primitive
types, such as int and char, in Java, values of primitive types are not objects.
■ Unlike C++, Java does not support multiple inheritance in general. Like Object-
ive-C, only a limited form of multiple inheritance, accomplished by the use of
interfaces, is supported in Java.
■ Unlike most other procedural and object-oriented programming languages, in
Java, the goto statement was eliminated.
■ Unlike most other programming languages, Java utilizes 16-bit characters instead
of 8-bit characters to support internationalization.
■ Java supports exception handling, automatic memory management (i.e., garbage
collection), and multithreaded programming. These mechanisms are considered
to be essential in object-oriented programming.
Perhaps the most notable feature of Java is its support for distributed computing.
Unlike most other programming languages that depend on platform-dependent add-on
utilities to support distributed computing, Java provides built-in langu·a ge constructs
and standard libraries for distributed computing.
LEXICAL ELEMENTS
Lexical elements are the basic building blocks of programming languages. We begin
with a look at Java's characters, identifiers, and literals. Then we discuss the operators
and expre sions used in Java.
4 .1 .1 Character Set
I . Tire
.
Unicode
. .
Standard
.
2.0 is defined in !The Unicode Consortium
•
1996] • un·11:0
. de reI ated ·1111•or-
mauon. rncludrng vanous tables and programs, can be obtained from lllc u,11· d C .
CO C UnSllf'tl Ulll 'II
http : //wvw . unicode . org. '
4.1 Lexical Elements • 77
The commonly used 7-bit ASCII character set is equivalent to the first 128 characters
of Unicode, known as the ISO Latin-12 character set, also known as IS0-8859-1.
The Java development environment can be localized to accommodate many different
locales. A locale is a country or region with distinct characteristics in culture and
language. The most widely distributed version of the Java Development Kit (JDK)
is localized to U.S. English. It performs the conversion between ASCII and Unicode
characters on the fly; that is, the U.S. English version of JDK reads and writes ASCII
files by default.
4.1.2 Identifiers
Identifiers are used in Java programs to denote the name of classes, methods, variables,
and so on. A Java identifier can begin with a letter, followed by letters or digits. In
Java, letters include the characters in the alphabets of all languages in Unicode, the
underscore (_), and the doIJar sign ($). Examples of Java identifiers are
Because Java programs are written in Unicode, you may u e the following as
identifiers in Java:3
jifj" 1tOAUyo><JcrtKO
Boolean Type
The boolean type consi t of two boolean literal : true and false. In Java, the
boolean type i not compatible with integer type . The re ult of comparisons are
of type boolean, not integer as in C and C++. The condition of control statements,
uch as if and while tatements ( ee Se tion 4.3.5 [p. 96] and 4.3.6 [p. 96]), are
e pe t d t be of type boolean, not integer type .
Integer Types
Java provides several integer types of different sizes.
However, Java provides no unsigned integer types. Integer literals can be written
in decimals, octals, and hexadecimals.
• A decimal integer literal begins with a nonzero decimal digit, followed by
decimal digits (e.g., 30).
• An octal integer literal begins with a leading zero (0), followed by octal digits
(e.g., 036).
■ A hexadecimal integer literal begins with a leading Ox or OX, followed by
hexadecimal digits (e.g., 0x1E and OX1e ).
The default type of integer literals are int . An integer literal may be followed by an
integer type suffix, 1 or L, to indicate that it is of type long (e.g., OxlL). The uppercase
letter Lis preferred, since the lowercase letter 1 could be easily mistaken as digit 1
(one).
Aoating-Point Types
Java provides two floating-point types.
Character Type
Most programming languages provide a single-byte (8-bit) character type that is ca-
pable of encoding up to 256 printable characters and control characters. Although
it is adequate for Western languages, which all have small alphabets, it is quite
inadequate for some Oriental languages, such as Chinese and Japanese. Modem Chi-
nese consists of more than 15,000 characters, of which some 6,000 are in common
daily use. For languages with large alphabets, a double-byte character type capa-
ble of encoding as many as 65,536 printable characters and control characters is
needed.
In the past, the vast majority of commercial software was developed and available
only in English. As nations' economies become more and more global, the huge
market potentials of non-English-speaking countries, such as the People's Republic
of China, have prompted commercial software vendors to port their software to
other languages. Microsoft, for example, released its Windows operating ystem in
several different languages simultaneously. Owing to the different size requirements
of characters, however, porting the English version of software (v here 8-bit characters
were used) to Oriental languages (where 16-bit characters are necessary) bas proved
to be extremely tedious and costly.
To support the internationalization of software. Java provide a single character
type char that is 16-bit unsigned and uses the Unicode character encoding.
Character Literals
ASCil characters can be written directly (e.g., a or C). Non-ASCII characters can be
written in their hexadecimal or octal codes.
-
80 • Elements of Java
String Literals
The String type is not a primitive type in Java. It is a class. However, string constants
can be written as literals. A string literal consists of a sequence of characters, including
escape sequences, enclosed by a pair of double quotes, as in the following examples.
See also "String Concatenation" in Section 4.1.4 [p. 84] and Section 4.4.8 [p. 118)
for operations on strings.
Java provides operators that are very similar to those in C and C++. These operators
and expressions are summarized in Table 4.1 . The leftmost column indicates the
precedence of the operators, with l as the highest precedence and 14 the lowest.
A 11u111eric type is either an integer or a floati ng-point type. All operator that can be
4.1 Lexical Elements ■ 81
applied to integer types can also be applied to type char. All the binary operators,
except the assignment operators, are left-associative. The assignment operators are
right-associative. Note the following examples:
Arithmetic Operators
The following arithmetic operators can be applied to all integer and floating-point
types.
X 4 y == X - (x / y) * y
The following examples show integer division and remainder and their relation-
ship:
7 I 3 2 -- * 2 + l
7--
7 ¾3 1
(-7) I 3 -2 (- 7) = = 3 * (- 2) + (- 1)
(-7) %3 - 1
82 • Elements of Java
TAILl4.1
Operators and Expressions
X y X I y X * y
The floating-point remainder is defined much the same as the integer remain-
der, or
X t. y == X - (x / y) * y
See 'The Float and Double Classes" [p. 130] and "Mathematical Constants and
Functions" [p. 13 l] for floating-point constants and commonly used mathematical
functions.
String Concatenation
The + operator can also be used to concatenate two strings. If one of the operands is
a string and the other is of some other type, the nonstring operand will be converted
to a string representation of its value and concatenated to the string operand, as in the
following examples:
Expression Result
"object" + 11
-
11
+ "oriented" Object-oriented
"object" + '-' + "oriented" Object-oriented
"Mail Stop"+ 205 Mail Stop 205
123 + ' ' + "Oak Street" 123 Oak Street
i++ 8 9
++i 9 9
i-- 8 7
--i 7 7
Relational Operators
The equality operator == and the inequality operator ! = can be applied to any type.
The comparison operators < Oess than), <= Oess than or equal to), > (greater than),
and>= (greater than or equal to) can be applied only to numeric types. All relational
expressions yield boolean results. The meaning of relational expressions on prim-
itive types follows customary conventions. We discuss the meaning of equality and
inequality of reference types in Section 4.2.3 [p. 91 ].
Logical Operators
The logical operators ! (negation), && (and), and 11 (or) can be applied to operands
of type boolean. The binary logical operators are conditional. That is, in expression
exp 1 && exp2, exp2 is evaluated only when exp 1 evaluates to true. Similarly, in
expression exp 1 11 exp2 , exp2 is evaluated only when e.--cp 1 evaluate to false.
-x Bitwi e complement of x
X &y Bitwise and of x and y
X I y Bitwise inclusi e or of x and y
X - y Bitwi e exclu ive or of x and y
Unlike the conditional logical operators, when the bitwise operators &, \, and - are
applied to operands of type boolean, both operand are always evaluated.
-
86 • Elements of Java
The shift operators can be applied only to integer types. The following shift
operators are supported in Java:
x « k Shift the bits in x k places to the left, filling in with Obits on the
right-hand side.
x » k Shift the bits in x k places to the right, filling in with the highest
bit (i.e., the sign bit) on the left-hand side.
x >» k Shift the bits in x k places to the right, filling in with Obits on
the left-hand side.
The following examples illustrate use of the bitwise and shift operators:
Expression Description
Conditional Operators
A conditional expression takes the form
where exp 1 must be of type boolean, and exp2 and exp 3 can be of any type. The
value of this conditional expression is exp2, if exp 1 evaluates to true, and exp 3, if
exp 1 evaluates to false . Examples of conditional expressions include the following:
Expression Description
Assignment Operators
An assignment operator is either the simple = or one of these:
+= -= I= <<= >>= >>>= I=
These operators are formed by concatenating a binary operator with =. The assignment
expression
is equivalent to
var= (var) op (exp)
except that var is evaluated only once in the original expressfon. When var is a
simple variable, these two expressions are always equivalent. However, they may
yield different results when var has side effects. These typically occur when array
indexes are involved. Let us assume that a is an integer array and that the initial value
of i is 2. The assignment expressions a [i ++] += i and a [i ++] = a [i ++] + i will
have different effects:
Expression Effect
Expression Description
A type denotes the et of all the legal value of that type. A variable refer to a location
in memory where a value can be tored. Each variable is associated with a type. The
variable type re tri ts the values that the variable may hold. Variable and their types
are declared in variable de larations.
88 • Elements of Java
Integer 0
Aoating-point O. O
char \uOOOO
boolean false
Reference null
Type conversion is the conversion of values of one type to values of another type.
We discu s lbe conversion of primitive types here and the conversion of reference
4.2 Variables and Types • 89
types in Section 5.2.2. Conversions between different numeric types are allowed.
There are two different forms of conversion.
The sizes of ranges of numeric types are ordered from small to large:
-1. Anny ure actunlly objt'l'l' too; sec i:ction 5.- ,'.! [p. 1701.
-
-
90 • Elements of Java
p : Point
X = 0
Garbage-collected y = 0
heap
.•····
Java, reference variables point to memory space that is dynamically allocated from
a garbage-collected heap. Programmers are thus freed from the responsibility of
managing the deallocation of memory.
Garbage Collection
Garbage collection refers to a mechanism that automatically deallocates unreferenced
or unreachable objects (i.e., garbage). It is one of the most important features of Java.
Garbage collection not only simplifies the programming task, but it also eliminates
one of the major sources of faults in programs involving memory leaks and dereferenc-
ing invalid pointers, which are common and notoriously difficult to trace and remove.
Garbage collection significantly reduces the amount of effort and time associated with
programming, testing, and debugging.
Whether to use garbage collection is perhaps one of the most hotly debated issues
in programming language design. Despite its obvious and significant advantages,
garbage collection imposes rather severe penalties on the performance of programs
of which it is a part.
Most garbage collection techniques involve exhaustive examination of the entire
memory space, either all at once or rotating through segments of the memory space
one at a time. Even with various improvements, such as generational garbage collec-
tion, garbage collection techniques are inherently time-consuming. Those that do not
involve exhaustive exarnination of memory space, such as reference counting, often
incur per-use penalties (i.e., a small penalty every time a variable is referenced), which
can become a significant cumul ative penalty. Techniques such as reference count-
ing also suffer from various limitations, such as circular references. In additi on, the
garbage collection proces could start at any moment, the timing of which is unpre-
4.2 Variables and Types ■ 91
dictable and usually beyond the control of the programmer. Therefore, programming
languages containing garbage collection are not suitable for developing hard real-
time systems that require the assurance of completing certain tasks within a specified
period of time.
To summarize, Java does not require the programmer to keep track of objects
created and destroy these objects when no longer needed. Java uses garbage collection
to manage automatically the deallocation of objects. Garbage collection does impose
some additional demands on hardware components. But, with the continuing increase
of CPU speed and drop in CPU and memory costs, the penalty on performance
becomes at least acceptable, if not negligible, for the vast majority of software
systems. The benefits of garbage collection far outweigh any penalty on performance
imposed by this mechanism.
See Sections 4.2.4 [p. 91] and 4.4.2 [p. 104) for the details of creating objects
and arrays.
Equality of References
For two reference variables rl and r2, the expres ion rl = r2 te ts to the equality
of the two references, not the equality of the swtes of the objects or array referred
to by them. In other words, r1 == r2 test the identity of rwo objects or arrays. To
test the equality (of the state ) of two object or array , the equals () method hould
be used, such as rl. equals (r2) . See the di cu sion of the equals O method in
Section 6.3.2 [p. 228].
4.2.4 Arrays
One-Dimensional Arrays
The elements in a one-dimensional array are indexed by the integers O to n - 1,
where 11 is the size of the array. A one-dimensional array of size n can be created
and initialized with either of the following methods:
new Type [ 11 ]
In this case the size of the array must be specified, and all the elements in the
array are initialized to the default initial values (see Section 4.2.1 [p. 88]) based
on the type of the elements.
2. Using the one-dimensional array initializer:
The values u0, u 1, ... , un-l are the initial values of the elements in the array. The
size of the array is determined by the number of initial values provided in the
array initializer.
int ial D = new int [3] ; Create an integer array of size 3; all elements
are initialized to 0.
int ia2 D = { 1, 2, 3}; Create an integer array of size 3; elements
are initialized to 1, 2, and 3, respectively.
Point pa □ = new Point [10] ; Create an array of points of size 10; all
elements are initialized to null.
Operation Description
Examples 4.1 [p. 97], 4.2 [p. 98], and 4 .9 [p. 133] illustrate the use of one-
dimensional arrays.
One of the main differences between the arrays in Java and those in C and C++ is
that Java arrays are always bound-checked at run time. Array bound-checking will au-
LomaticaJly detect a major source of faults in programs, that is, exceeding the bounds
◄
4.3 Statements • 93
Multidimensional Arrays
A multidimensional array is treated simply as an array of arrays. Let a be a k-
dimensional array; the elements of a can be accessed using the following syntax:
The size of each dimension is n 1, n 2 , ••• , n k, respectively. All the elements in the
array are initialized to the default initial values (see Section 4.2. l [p. 88]) based
on the type of the elements.
2. Using the k-dimensional array initializer:
The following are examples of creating and initializing two-dimen ional arrays:
double matl[J [J = new double[4J [SJ;
4.3 STATEMENTS
The syntax and emantics of ' tatement in Java are nearly identical to those of C and
C++. There i one exception: The goto tatement was eliminated. A early as 1966,
Bohm and Jacopini [ 1966] pointed out that the goto tatement i. unneces. ary. That is,
any program containing goto ·tatement , can be convened to a functionally equivalent
program without goto tatements. ln 196 , Dijk tra made perhap. the best known
a ertion about the goto statement. con ·idering it to be hannful [Dijkstra 1968].
More than 30 years later. the goto statement remain in almo t every programming
--- -
94 • Elements of Java
Expressions for object creation and method invocation can also be made into
statements by appending a semicolon to the expressions, as in
pointl = new Point() ;
poi ntl.move ( lO, 10);
i a = new int[3] ;
We discuss object creation and method invocation in Sections 4.4.2 [p. 104] and
4.4.4 [p. 107].
Staremenln
}
For example,
{
inti= O;
prime[i++] = 2;
prime[i++] = 3;
prime[i++] = 5;
}
Local variable declarations are also simple statements. They must be terminated by a
semicolon (; ). Local variable declarations can occur anywhere in a statement block
and can be intermixed with other statements. The variables declared are called local
variables. Each variable declaration has a scope, which is the extent of the code in
whkh the variable is visible, that is, accessible. The scope of a local variable begin
at its declaration and extends to the end of the immediate enclosing tatement block.
For example,
The return tatement terminate the execution of a method and return control 10
its caller. The yntax of the return tatement i
return [fapressio11] ;
The type of the expre ion mu t match the return type of the method that contains
the return tatement. The return tatement may not have an ex pres ion if the return
type of the method that contain the return tatement i void. (See Section 4.4.4
[p. l 07] for a di cu sion of method .)
-
96 • Elements of Java
case Casel.Abeln :
Statementn
default :
Statemenrn+I
}
The expression following the svi tch keyword must be an integer expression.
The case labels in switch statements are different from statement labels. Case labels
must be constant integer expressions. Each branch in a switch statement may have
one or more labels. Although the default branch is optional, you should include the
default branch at the end.
The execution of a switch statement begins with evaluation of the integer
expression and comparison of the result with each case label sequentially. The first
branch with a matching case label will be executed. If none of the case labels match
the result of the expression, the default branch will be executed. If the default branch
is absent. the entire switch statement is skipped. Each branch is usually terminated
by a break statement, in which case execution continues with the statement following
the switch statement. If a branch is not terminated by a break statement, execution
will fall through and continue with the next branch of the switch statement. This
f all-through feature allows several branches to share common code.
Statement
vhile ( Condition )
Statement
lnitExpr;
vhile ( Condition ) {
Statement
lncrExpr;
}
The following are two simple examples using the loop statements to manipulate
one-dimensional arrays.
PURPOSE
To illustrate the for loop statement and one-dimensional arrays.
DESCRIPTION
The following program calculates the sum of the values in the array.
SOLUTION
The sum of an~ of In :Saa.java
public class Sum {
public static void main(String[]args) {
int a[] : 1, 2, 3, 4, 5, 6, 7, 8 , 9, 10;
int sum== O;
for (inti: O; i < a.length; i++) {
sum+= a[i);
}
System.out.println("The sum is : 11
+ sum);
}
}
PURPOSE
To illustrate nested loop statements and one-dimensional arrays.
DESCRIPTION
The following program- using the bubble sort algorithm-sorts the array in ascend-
ing order and prints the sorted array to the standard output.
SOLUTION
Statement Labels
Each statement can have an optional label, which is simply an identifier. A labeled
statement has the syntax
[StatementLobel : ] Statement
break [Statementlobel] ;
continue [Statementlobel] ;
With the preceding pair of nested loops, the break statement will break out of
the inner loop but not the outer loop. Using a break statement to break out of an inner
loop and outer loop simultaneously also is known as multilevel breaking. Sometimes,
multilevel breaking is desirable. Let us consider the two-dimensional array
double matrix[][);
We try to determine whether all the values are nonnegative. The following is a solution
in C and C++, which uses a goto statement to break out of the nested loop.
boolean nonNegative = true ;
for (inti= O; i < matrix.length; i++) {
for (int j = O; j < matrix[i) .length; j++) {
i f (matrix[i) [j) < 0.0) {
nonNegative = false ;
goto Done;
}
}
}
Done : // . ..
One way of eliminating the goto statement requires a new boolean variable,
isDone, and a more complicated loop condition for the outer loop.
ln Java, however, the labeled break statement can be used to break out of the
outer loop.
Using the labeled break statement, we can accomplish the same task without
introducing unnecessary boolean variables.
4.4 Class Declarations • 101
Classes are the basic compilation units, that is, units that can be compiled individually,
of Java programs. A Java program consists of one or more class declarations. A class
declaration defines a class, which also defines a reference type.
A class declaration consists of the class name and a sequence of fields. methods,
and nested class declarations. The fields of a class are also known as the variables
or attributes of the class. The syntax of class, field, and method declarations is
summarized as follows:
A class declaration may begin with a list of class modifiers, which are umma-
rized as follows:
Each file can contain only one cla s that i declared public. The name of the file
and the name of the public class it contain must coincide, and the file mu t have
the extension . j ava. For example, the cla declaration of public cla Point must
be stored in a file named Point . j ava. Each file may al o contain any number of
nonpublic classes.
The extends clau e pecifie the upercla of this clas . The implements
clause specifies the interface being implemented by thi cla s. We di cu extending
classes in Section 5.2 [p. 163], and implementing interface in Section 5.3 [p. I76).
The body of a class declaration con i t of a Ii t of member declarations. Member
declaration can be declaration of fields, methods, and nested classes. The fiel~,
method, and ne ' ted cla declaration can be intemuxed. The order of declarations is
immaterial to the compiler. Thu , they hould be ordered in a way that is most logical
and comprehen ible.
102 • Elements of Java
A nested class declaration has the same syntax as the top-level class declaration .
Each class member declaration begins with an optional list of modifiers. Modi-
fiers that can be applied to method, field, and inner class declarations include
~nly one public, protected, or private modifier may appear in the modi-
fiers hst for each member. Member accessibility is summarized in Table 4.2.
4.4 Class Declarations • 103
* Yes: Accessible.
t No: Not accessible.
Method Declarations
The return type of a method is required. If a method does not return a value, the return
type should be specified as void. A method may take a list of parameter declaration
separated by commas (the list could be empty). Parameters are declared in the form
A fi nal parameter is one that cannot be assigned a value inside the method (see
" Pitfall: Final Parameters" in Section 4.4.4 [p. 109]).
The following class declaration declares a simple Point cla , which repre ents
points in a two-dimensional space.
There are four ways of initializing the fields of a class: an explicit initializer in a
declaration, default initial values, constructors, or an initialization block.
Explicit Initializer
A field can be assigned an initial value in the declaration with an explicit initializer.
For example, the x and yin the Point class can be explicitly initialized as
public double x = 0 . 0, y = 0.0;
Default Initial Values
lf the fields are not explicitly initialized, they are implicitly initialized to their default
initial values (see Section 4.2.1 [p. 88]).
Constructors
Another way to initialize fields is to use constructors. Constructors are special meth-
ods that have the same name as the class; their return type must be omitted. A class
may have multiple constructors, provided that they take different numbers of argu-
ments or arguments of different types.6 The following is an enhanced Point class
declaration with two constructors.
public class Point {
public double x, y;
public Point() { If no-arg constructor
X = 0.0; y = 0 . 0;
}
of the Point class in the preceding example were provided, creating instances of the
Point class with the no-arg constructor would still be legal, as in
The instance p3 would be initialized to (0, 0) by virtue of the default initial values.
When constructors are provided explicitly, the default no-arg constructor is no longer
provided implicitly. It must be provided explicitly if it is desired. In other words, if the
no-arg constructor of Point class were removed but the other constructor remained,
new Point O would be illegal. Hence, in general, the no-arg constructor should be
provided for all classes.
Initialization Block
Fields can be also initialized by means of an initialization block, which is a statement
block directly enclosed in the class declaration. The initialization block is executed
before the body of any constructor is executed. The initialization block can be used
to avoid duplicating identical statements for initializing fields in everal constructors.
These identical statements can be factored to the initialization block. This block is
needed only when the initializations are too complicated for initializers. such as those
that require loops. For example, the following Card and Deck classes model the cards
and decks used in card games such as bridge:
The initialization of cards is more suited to the use of loops than enumerating
all 52 cards in an array initializer. The Deck class may have several constructors.
Placing the initialization of cards in the initialization block avoids duplication of
code in several constructors.
After an instance has been created, the fields and methods of the instance can be
accessed as follows:
objectReference .method (Param eters)
objectReference .field
For example,
Point pl= new Point();
double xl = pl . x; // the value is 0.0
double yl = pl. y; II the value is 0.0
p1 .move(10.0 , 20 . 0) ;
double x2 = pl .x; // the value is 10.0
double y2 = pl . y; // the value is 20.0
In tenns of Java reference variables, memory space for objects must always be ex-
plicitly allocated on the garbage-coJJected heap by the new operator. Java reference
variables are analogous to C++ pointer variables but use the syntax of C++ object
variables for accessing members. In Java, deallocation of memory space is automati-
4.4 Class Declarations ■ 107
Implementing Methods
The body of a method is simply a block statement. If the return type of a method is
void, the return statements in the method body may not return values. If the return
type of a method is not void, all the paths of the method body must be terminated by
a return statement with an expression that matches the return type. The following
calculateitemTotal O method will cause a compilation error because the path
of quantity < 0 is not terminated by a return statement.
To correct this error, we must add a return statement to return a value when the
quantity is negative, or an exception must be thrown ( ee Section 4.6 [p. 139)).
The local variables declared in method bodies are not automatically initialized to
their default initial values. Local variables must be explicitly initialized on all paths
that lead to their uses. For example, the following calculateitemTotal method
w ill cause a compilation error because the local variable total i not initialized on
the path of quantity < 0.
7, When some cleanup must to be done before an object is dc:illoca1ed, such as closing files or flushing
changes to files, the f i nalize () method in the Ob j ect cl:is can be useJ .
-
108 • Elements of Java
or
Parameter Passing
In Java, all parameters of methods are passed by value. In other words, modifications
to parameters of primitive types inside a method will be made on copies of the actual
parameters and will have no effect on the actual parameters themselves. Consider the
example of class Cl,
public class Cl {
public void inc(int i) { i++; }
}
Cl cl s new Cl();
int k • 1;
c 1. inc (k) ; // k ls still 1 afterward
For parameters of reference types, the fields of the actual parameters can indeed
be affected inside a method. Consider the example of class C2,
public class C2 {
public void pointlnc(Point p) { p.x++; p.y++; }
}
4.4 Class Declarations • 109
C2 c2 = new C2();
Point p = new Point(l0.0, 10.0) ;
c2. pointlnc (p) ; // now p is (11 .0, 11 .0)
Hence parameters of reference types can serve as in-out parameters. In order for
a parameter of primitive type to do so, it must be wrapped inside a class. Consider
the example of an integer wrapped in the IntRef class so that it may act as an in-out
parameter,
class IntRef {
public int val;
public IntRef(int i ) {val= i; }
}
public class C3 {
public void inc(IntRef i) { i .val++; }
}
C3 c3 = new C3();
IntRef k = new IntRef(l ) ;
c3. inc (k) ; // now k.val is 2
Usually, a method passes values back to its caller via the return value of the
method. In-out parameters are necessary only when the method need to pa multiple
values back to the caller.
This code segment will cause a compilation error becau e it i not allowed to a. ign
a new value to a final parameter. However, the following method i allowed:
Java final parameter arc not the ame a const parameters in C++. Further-
more, there i no Java counterpart of C++ const method. , \ hich do not modify the
state of the receiving object.
11 0 • Elements of Java
By default the fields declared in a class are instance fields, which means that each
instance of the class carries its own copy of these fields, and modifications to the
fields of one instance will not affect any other instances. In contrast, class fields are
shared by all the instances of the same class. There is only one copy of each class field
for the class. Modifications to class fields will affect all the instances of the class. By
default the methods of a class are also called instance methods, which means that they
operate on a specific instance of the class when they are invoked. If a method accesses
only class fields, then it is called a class method. Class fields and class methods are
declared using the static modifier. Thus they are also often called static fields and
static methods.8
Instance fields and methods can be accessed only through an object reference.
Class fields and methods may be accessed through either an object reference or the
class name.
Class and instance fields and methods are subject to the same accessibility controls.
Class fields and methods should be accessed through class names, not object refer-
ences. Then instance fields and methods can easily be distinguished from class fields
and methods by the way they are accessed.
The following is a simple class Robot representing the position of robots. It con-
tains an instance field instanceMoveCount, which counts the number of moves
made by each instance of the Ro bot class. Its value can be retrieved by an instance
method getlnstanceMoveCount () . The Robot class also contains a class field
classMoveCount, which counts the total number of moves made by all instances of
the Robot class. Its value can be retrieved by a class method getlnstanceMove-
Count O. The method getClassMoveCount () is a class method because it accesses
only a class field.
8. The term sr~ric refers~ the way class fields and melhods a.re implemented. Class fields and merhods a.re
more approp~ate tenns, since Lhey ~ nnote their behavior as being per-class versus per-instance. Although
the term are interchangeable, we will use Lhe term class fields and methods throughout Lhi s book.
4.4 Class Declarations • 111
Constants
In Java, constants can be declared as final class fields. For example, several integer
constants for specifying font styles are defined in the Font class.
We may also define constant objects. In the Color class, some commonly used
colors are declared as follows.
Name of constants should be in all uppercase lelters (e.g., the constants in the
Font and Color class).
When there are multiple words in the names, separate the words with underscores
{_) (e.g., STANDARD_RATE, MAXIMUM_CLASS _SI ZE).
4.4 Class Declarations • 113
The main () method serves as the entry point of a Java application. Java applications
are invoked as
venus¾ j ava ClassName [arguments . . . ]
PURPOSE
To illustrate retrieving command-line argument passed to the ma i n () method of a
Java application.
DESCRIPTION
The following program prints the command-line argument to the tandard output.
SOLUTION
Print command line ~ t s : ~enta .java
public class Arguments {
publi c static void main(String [] args) {
if (args . length > 0) {
for (inti= O; i < args . length; i++) {
System.out.println ( "args[" + i + "] : " + args [i]) ;
}
} else {
System.out.println(" No arguments . ") ;
}
}
}
the result is
args[O] : foo
args[l]: bar
114 ■ Elements of Java
Singleton Classes
Some classes are not supposed to have more than one instance at a time. Examples
are the top-level window of an application and the timer of an entire system. Classes
that can have no more than one instance at a time are called singleton classes. Using
class fields and methods, we can ensure that no more than one instance of a singleton
class exits at any given moment. The following is a schematic implementation of a
singleton class:
public class Singleton {
static public Singleton getlnstance() {
return thelnstance;
}
protected Singleton() {
II initializing Instance fields
}
The unique instance of the Singleton class is stored in a class field, the In-
stance. Note that the constructor of the Singleton class is protected. Therefore,
clients are prohibited from creating an instance of the Singleton class by doing
The get Instance O ensures that a new instance of the class will be created only
when it is invoked the very first time. Hence, just one instance of the Singleton
class can be created!
Instance methods operate on a specific instance of a class. This object instance is often
referred to as the receiving instance (also known as the receiver or the recipient ) of
the instance method, that is, the object instance through which the instance method is
invoked. Inside instance methods, a special object reference this is used to denote
the receiving instance. Since class methods operate on class fields only, the object
reference this may not be used in class methods.
The two common uses of this are (1) to pass the receiving object instance as
a parameter to other methods and (2) to access instance fields that are shadowed or
hidden, by local variables. '
4.4 Class Declarations • 11 5
Each faculty member has a name and a reference to the department to which the
faculty member belongs.
II . . . other methods
}
Each department has a name and a list of faculty member who belong to the
department. For consistency, it is important to maintain the following requirement
among the instances of the e two classe :
. , __ _ _ _ _ _ _ _ _ _ _ _ _ _ , I
11 6 ■ Elements of Java
Java adopts the usual scope rule: When a name in an outer scope is shadowed
by a name in an inner scope, the name in the outer scope is hidden. In the case of a
shadowed instance field of a class, the shadowed field var can be accessed via the
receiving object reference this as this. var. In general, the shadowing of variable
names in outer scopes is a bad programming practice and should be avoided, as it is
a common source of program bugs. In contrast, the shadowing of instance fields of
a class from within its methods is an acceptable and common practice, if the local
variables are indeed copies of the fields of the same class. The purpose is to avoid the
confusion of introducing a new set of variable names to mirror the fields of the class.
The following program segment is another version of the Point class.
II . .. other methods
}
In the preceding constructor, parameters x and y represent the initial values of the
correspondi~g instance fields of class Point . The expressions this. x and this . y
refe_r to the instance field. In method adjustPosition(), the instance fields are
copied to the local variables with the ame names. Calculations are performed o n
..
4 .4 Class Declarations ■ 11 7
these local copies. Only when the calculations have been completed will the results be
committed to the instance fields of class Point. Therefore, no intennediate values are
ever stored in the instance fields. The advantages of this approach are to avoid leavino
e,
intermediate results in the instance fields in case an exception is encountered (see
Secti on 4.6 [p. 139]) and to avoid synchronizing an entire method of a multi threaded
program. Only the reading and writing of instance fields hould be synchronized. The
calculation portion can be unsynchronized (see Section 11 .2.1 [p. 557]).
Interfaces can be thought of as a special form of cla s, which declare only the
features to be supported by the class. Java interfaces provide no implementation.
Implementation is deferred to the classes that implement the interface. The yntax
of an interface declaration is
Interface member can be either abstract methods or con tant (that i , tatic and
final fields). Implementation of ab tract method i deferred to ubcla e . Ab tract
methods are declared as
All interface are public, and all method and con tant declared in interfaces
are public. The public modifier can be omitted in interface de laration . Class
( ta tic) methods are not allowed in interface . Interface have no con truetor , and
no in tances of interface are allowed.
Here are two commonly u ed interface in Java: the Runnable interface,
An abstract class is a class that includes or inherits at least one abstract method.
In other words, an abstract class is a class with partial implementation. Although the
presence of an abstract method in a class implies that the class is abstract, the class
must still be declared abstract explicitly; that is, the class modifier abstract must
be present for the sake of readability. Like interfaces, an abstract class may not have
instances. We discuss the implementation, extension, and use of interfaces in detail
in Chapters 5 and 7.
4.4.8 Strings
A string is a sequence of characters. The String type is not a primitive type in Java;
it is a class type. Java provides two classes to support strings: (1) the String class,
whose instances are immutable (that is, constant strings), and (2) the StringBuff er
class, whose instances are mutable strings. The rationale for distinguishing mutable
from immutable strings is that in a typical program most strings are immutable.
Immutable strings allow simpler implementation and a more compact representation
than do mutable strings.
The String class is special in the sense that it enjoys some unique privileges not
shared by ordinary classes.
• A string, that is, an instance of the String class, can be created using string
literals (see "String Literals" in Section 4.1.3 [p. 80]).
• Operators + and += can be applied to strings (see "String Concatenation" in
Section 4.1 .4 [p. 84]).
The characters in a string are indexed by integers Oto n - 1, where n is the length
of the string. The common operations on strings are summarized in Table 4.3.
The StringBuffer class provides an overloaded append() method that ap-
pends the text representation of its arguments to the string buffer.
String Comparison
For two string variables s 1 and s2, s 1 == s2 tests the equality of the two references
not the equality of the strings referenced by them. Thus s 1 == s2 is true if and
-----------~
4.4 Class Declarations • 119
TABLE 4.3
Methods of String Class
Method Description
only if both s1 and s2 reference the ame tring (that i . point to the arne memory
location). The equivalence of two tring can be te ted in everal ways:
s1. equals(s2)
s1.equalsignoreCase(s2)
s1 . compareTo(s2)
compares the two strings lexicographically, according to the Unicode; the comparison
is case-sensitive and returns an integer value of
The following code segment illustrates the differences between comparing string
references and string contents. Three strings strl, str2, and str3 are initialized in
different ways as follows:
String strl = "FooBar";
String str2 = strl;
String str3 = new String("FooBar");
The first two strings strl and str2 reference the same string object, while str3
is a different string object with the same contents.
strl~
~
str3~
str2
The following table shows different ways of comparing the strings and their results:
Expression Result
--
strl -- str2 true
strl -- str3 false
str1 . equals(str2) true
str1 . equals(str3) true
strl.compareTo(str2) O
strl.compareTo(str3) 0
Note that comparisons of the identity and equality of strings do not always yield
the same results. String comparison is a very common operation in many programs.
Comparing the identity of two strings takes far less time than comparing the equality
4.4 Class Declarations ■ 121
of tw? str_ings. ~he String class supports a canonical representation of string objects.
It mamtams an internal pool of unique string objects. A tring object can be interned
by calling the intern () method of the String class, which returns a string that has
the same contents as the original string, but is guaranteed to be from the internal pool
of unique strings. For interned strings, comparison of the identity and equality of the
strings always yield the same results.
The following example expands the preceding example to illustrate the differ-
ence between comparisons of lhe identity and equality of tring , and lhe effect of
interning:
Note that all string literals are automatically interned (compare the results con-
cerning strl, str4, and str5). In most cases, interning strings will significantly
improve the performance of programs.
}
}
If we assume that str is a string variable and that obj is a variable of class type,
the expression
str + obj
is interpreted as
str + obj.toString()
4.4 Class Declarations • 123
N?w, having added the toString () method to the Point class, we may do th
following. e
is equivalent to
and
is equivalent to
The Java input and output mechani mi. both er' atile and nontrivial (we discus
it in detail in Section 8.4 [p. 366)). We begin \ ith ome imple u e of the input and
output mechani m to read and \ rite tring .
........... -
124 • Elements of Java
PURPOSE
To illustrate reading and writing strings.
DESCRIPTION
The following program reads the characters read from the standard input line by line
and writes each line to the standard output.
SOLUTION
from standard
import java.io . *;
public class Copy {
public static void main(String[] args) {
try {
BufferedReader in= new BufferedReader(
new InputStreamReader(System . in));
String line;
while ((line= in.readLine()) != null) {
System . out.println(line);
}
} catch (I□Exception e) {}
}
}
Here, the standard input and output are redirected to two files named in-
file. txt to outf ile. txt, respectively. The preceding invocation of Copy copies
infile. txt to outfile. txt.
PURPOSE
To illustrate reading and writing text files.
DESCRIPTION
The following program simply copies the contents of one text file to another text file
line by line. '
4.4 Class Declarations ■ 125
SOLUTION
Copy a text file: CopJ1extF1le. j ava
import java.io.*;
public class CopyTextFile {
public static void main(String[] args) {
if (args . length >= 2) {
try {
BufferedReader in= new BufferedReader (
new FileReader(args(O]) ) ;
PrintWriter out= new PrintWriter (
new BufferedWriter (
new FileWriter(args[l] )) );
String line;
while ((line= in.readLine ()) != null) {
out.println(line) ;
}
out.flush();
out. close() ;
} catch (IOException e) { }
}
}
}
To extract the information stored in the colon-delimited record format, each record
need to be divided into fields. One way to break trings into token i to use the
indexOf () and substring () method of the String cla s.
126 • Elements of Java
PURPOSE
To illustrate string manipulation.
DESCRIPTION
The following program reads colon-delimited records from the standard input and
breaks each record into fields.
SOLUTION
8nak colon-dellmlted records: BreakRecorda . j ava
import java.io.*;
public class BreakRecords {
public static void main(String[] args) {
BufferedReader in= new BufferedReader (
new InputStreamReader(System.in));
try {
String record, field;
char delim =' : ' ; II the delimiter
I for (int n = 1; (record= in . readLine()) != null; n++) {
I, • System.out . println( 11 Record 11 + n) ;
int begi n, end, i;
begin= O;
for (i = O; (end= record.indexOf(delim, begin)) >= O; i++) {
field= record.substring(begin, end);
begin = end + 1 ; // skip the delimiter
System . out.println( 11 \tField 11 + i + ": "+ field);
}
field = record. substring(begin) ; // the last field
System . out.println("\tField" + i + ": "+ field);
}
} catch (IOException e) { }
}
}
The following output of the program is obtained when the two name and address
records shown previously are used as input:
Record 1
Field 0: Michael
Field 1: Owen
Field 2: 123 Oak Street
Field 3: Chicago
Field 4: IL
Field 5: 60606
... .,.. .·
Record 2
Field 0: James
Field 1: Gosling
Field 2: 456 Sun Blvd.
Field 3: Mountain View
Field 4: CA
Field 5: 45454
The second approach breaks strings into tokens with the help of the StringTo-
kenizer class.
PURPOSE
To illustrate the use of StringTokenizer.
DESCRIPTION
The following program reads text from standard input and break the text into words.
SOLUTION
Break text into words: Words. Java
import java.io.• ;
import java . util.• ;
public class Words {
public static void mai n (Stri ng[] args) {
BufferedReader in=
new BufferedReader (new InputStreamReader (System. in) ) ;
try {
String line , word ;
String delim = "\ t\n. , : ; ? !-/ () (J \ " \' " ; // spaces and punctuations
while (( line= i n . readLi ne ()) != null ) {
StringTokenizer st= new Stri ngTokenizer (line , delim) ;
while (st . hasMoreTokens ( ) ) {
System. out. pri nt ln (st .nextToken ());
}
}
} catch ( IOExcept i on e) {}
}
}
The con tructor call in Example -l.7 create an in tance of the StringTo-
kenizer cla s that discards the delimiters indkated by the tring delim. The
StringTokenizer class has two other con tructor . All three StringTokenizer
constructors are sunmuuized in the fo lio\ ing table. Parameter srr is the string to
128 • Elements of Java
Constructor Description
Method Description
Because in Java values of primitive types are not objects, a wrapper class is provided
to "wrap" the values of primitive types into objects when needed. Each primitive type
has a corresponding wrapper class as shown in the following table:
boolean Boolean
byte Byte
char Character
double Double
float Float
int Integer
long Long
short Short
4.4 Class Declarations • 129
Wrapper classes allow values of primitive type to be used in place where objects
are expected, as in the elements of collection classes such as List or Hashtable
(we discuss collections in Section 8.2 [p. 308]). Wrapper classe also provide useful
methods and constants for manipulating the values of primitive type .
Seemingly, instances of wrapper classes could also be used for in-out parameters
for primitive types. Unfortunately, however, they may not. The rea on i that instances
of wrapper classes are immutable; that i , they provide no way to modify the tate of
their instances.
If we let Type be a wrapper class of primitive type named f)pe, for each wrapper
class Type at least two constructor are provided. One talces a value of f)pe, and the
other takes a string representation of a literal of f)pe. For example, the Integer cla s
has the constructors
Integer(int value)
Integer(String value )
Instances of wrapper classes can also be created by using the cla method val-
ue □f (). The val ueOf () method of wrapper class Type talces a string repre entation
of a literal of type and returns an instance of Type. Each wrapper cla al o provide
methods for retrieving its value as a primitive type.
Boolean booleanValue ()
Character charValue 0
Byte byte Value()
Double double Value ()
Float float Value()
Integer intValueO
Long longValue ()
Short short Value ()
For a tring repre entation of a literal of primitive type type, the value re pre ented
by the literal can be obtained a
Type . valueOf (literal). typeValue ( )
For example, the following expre ion give the re ult hown:
Expression Result
The class method parse Int () of the Integer class can also be used to parse
integers. It parses a string representation of an integer literal and returns an int
value. For example, the expression Integer. parse Int (" 100") produces the result
100.
A common use of the parsing method is to parse the input values to a program,
which are often provided as strings.
PURPOSE
To illustrate the use of the parse Int O method to parse command-line arguments.
DESCRIPTION
The following program takes two integers as command-line arguments. It converts the
string arguments into integers using the parse Int () method and finds the maximum
of the two integers.
SOLUTION
1be DMIUJIIIIID of two in ~ents: Muimua. java
public class Maximum {
public static void main(String[] args) {
if (args.length >= 2) {
int i1 = Integer . parseint (args[O]);
int i2 = Integer . parseint(args[1]);
System . out .println("The maximum of"+ i1 +" and 11 + i 2 +
II is; II + ((i1 >= i2)? i1 ; i2) );
} else {
System.out .println( 11 Usage: java Maximum integer! integer2 11 ) ;
}
}
}
The following instance methods are declared in both classes. These methods can be
used to test whether the value wrapped in the instance i NaN or infinity.
In Double In Float
And the following class methods are also declared in both classes. These methods can
be used to test whether a parameter is NaN or infinity, such as
In Double In Float
TAILE 4.4
Mathematical Constants and Functions
Constant/Method Description
double E e = 2.7182818284590452345
double PI rr = 3.14159265358979323846
double sin(double a) sin(a), a an angle in radians
double cos(double a) cos(a), a an angle in radians
double tan(double a) tan(a), a an angle in radians
double asin(double a) arcsin(a), result in [-rr /2, rr /2]
double acos(double a) arccos(a), result in (0, rr]
double atan(double a) arctan(a), result in [-rr/2,rr/2]
double atan2(double a, double b) arctan(b/a), result in [-rr, rr]
double exp(double a)
double pow(double a, double b)
double log(double a) ln(a), the natural logarithm of a
double sqrt(double a) ../a, the square root of a
double rint(double a) truncated integer value of a
double random() a pseudorandom number in (0.0, 1.0)
double ceil(double a) fa l, the ceiling of a
double floor(double a) LaJ, the floor of a
int round(float a) La+ 0.5J , the rounding of a
int round(double a)
int abs (int a) la I, the absolute value of a
long abs(long a)
float abs(float a)
double abs(double a)
int max(int a, int b) max(a, b), the maximum of a and b
long max(long a, long b)
float max(float a, float b)
double max(double a, double b)
int min(int a, int b) min(a, b) , the minimum of a and b
long min(long a, long b)
float min(float a, float b)
double min(double a, double b)
4.4 Class Dedarations • 133
PURPOSE
To illustrate the use of mathematical functions in Math class.
DESCRIPTION
The following program finds the minimum value of an array.
SOLUTION
1bf' mlnlmlJDl of-an &rnJ' ofln~rs: Minim.java
public class Minimum {
public static void main(String[]args) {
int a[] = { 75, 34, 80, 11, 95 , 34, 53, 81, 33, 13 };
int min= a[O];
for (inti= 1; i < a. length ; i++) {
mi n= Math.min (min , a[i]);
}
System . out . println ("The minimum value is: 11
+ min);
}
}
PURPOSE
To illustrate the use of mathematical functions in Math class and handling of two-
dimensional arrays.
DESCRIPTION
Assume the following declaration of a matrix of double :
double mat [] [] ;
The following program segment find the max- min value of a two-dimen ional array,
that is, the maximum of the minimum of each column, or
max min mat[i][j]
O~ j-:;m O~i ~ 11
SOLUTION
1be mu-min value of a two-dimensional arra)': Mu:Min. ava
public class MaxMin {
public static void main(String[]args) {
double mat[](] = { { 2 . 3, 5.1, 9.9 },
{ 8 . 3, 4.5, 7.7 },
{ 5 . 2, 6.1, 2 . 8} };
int n = mat.length;
int m = mat(O] . length;
double maxmin = 0 . 0;
for (int j = O; j < m; j++) {
double min= mat(j] [OJ;
for (inti= 1; i < n ; i++) {
min= Math.min(min, mat[i] [j]);
}
if (j == 0) {
maxmin = min;
} else {
ma.xmin = Math . max(maxmin, min);
}
}
System.out . println("The max-min value is"+ maxmin);
}
}
PACKAGES
Classes are the basic building blocks of Java programs, and a Java program consists
of one or more classes. Classes should be relatively small and comprise highly
cohesive functionalities. Because a large program may consist of thousands of classes,
providing a mechanism for logically organizing large programs is necessary. Java
provides two such mechanisms:
1. Files, which may contain a main public class and possibly a few nonpublic helper
classes.
2. Packages, which comprise many related classes, interfaces, or other packages.
Files are the compilation units of Java; that is, each file can be compiled separately.
Packages support hierarchical organization and arc used lo organize large programs
into logical and manageable units.
4.5 Packages • 135
In Java each class belongs to a package. Package declaration is file based; that is,
a11 classes in the same source file belong to the same package. Each source file may
contain an optional package declaration in the following form:
package PackageName ;
The package declaration at the top of the source file declares that the Point clas
belongs to the package named geometry. When the package declaration is ab ent
from a file, all the classes contained in the file belong to an unnamed package.
Packages serve as a useful mechanism for grouping closely related classe and
interfaces. The classes that belong to a package should be clo ely related because a
class may access not only the public fields and methods of other classes belonging to
the same package, but also all except the private field and methods of these clas e .
A class in a named package can be referred to in two different way :
PackageName. ClassName
geometry . Point
2. Importing the class and using the simple cla nan1e. We can import a cla in
the designated package using
or, we can import all the classes in the designated package u ing
import PackageName. • ;
The Point class in package geometry can imply be referred to a Point when
either of the following import clau e occurs at the top of the source file:
Some of most fundmuental and mo ·t commonly used cla e are defined in the
j ava. lang package. The j ava. lang package i implicitly imported by all Java
programs. So it is unnece ary to explicitly import the j ava. lang package.
136 • Elements of Java
Packages serve as a useful mechanism for partitioning name space and preventing
name collisions. When they belong to different packages, classes with the same name
can be used by the same class without causing name collision. The Java Class Library,
for example, contains another Point class, whose fields x and y are declared as
type int instead of type double. This other version of the Point class is defined in
package java. awt. However, both classes can be used by the same class by using
their fully qualified names.
geometry.Point= new geometry.Point(10.0, 20 . 0);
java.awt.Point = new java.awt . Point(10, 20);
Alternatively, we can use the import clause to import one of the classes so that
it can be directly referenced using only its class name.
import geometry;
In practice, all classes are placed in packages for all applications. The basic rule
for placing classes in packages is stated in the folJowing guideline:
When pack.ages are used, source and class files must be placed in directories whose
structures match ~he structures of the packages. Several directory paths are important
to the Java compiler, j avac, as described in Table 4.5.
4.5 Packages • 137
TABLE 4.5
Directory Paths
Path Description
The source directory root This is where the compiler looks for the ource files.
The default is the current working directory.
The destination directory root This is where the compiler places the cla files (that
is, the files that the compiler generates). The default is
the current working directory. It can be pecified on the
command line by using the -d option.
The CLASSPATH A list of directories and/or jar files. The compiler will
look in these directories for any precompiled cla file
it needs. The CLASSPATH can be et as an environment
variable or specified on the command line by using the
-classpath option. The CLASSPATH is al o u ed by
the Java Virtual Machine to load cla es.*
* Consult the JDK/JRE installation guide for instructions on etting the CLASSPATH environment variable
on specific platforms.
For example, let srcdir be the source directory root and classdir be the desti-
nation directory root. Let us assume that we have a clas Foo in package pack-
age 1. package 2. In this case, the source file Foo. j ava mu t be located at
srcdir/package I/package2/Foo.java
}
}
}
Assuming srcdir is the source directory, the source file Maximum. j ava must be
placed in the subdirectory classdir /xj /num. To compile the program, change the
current directory to srcdir and do
venusi. java xj/num/Maximum.java
In this case, the srcdir directory is also the destination directory. The Java compiler
generates the byte-code file Maximum. class in the same directory as the source code
file, that is, classdir/xj/num. To run the program, remain at the srcdir directory and
do
venusi. java xj .num.Maximum 11 12
The maximum of 11 and 12 is: 12
You may choose to use a destination directory, say destdir, that is different from
the source directory. To compile the program, change the current directory to the
source directory srcdir and do
venusi. j ava -d destdir xj /num/Maximum . j ava
The Java compiler generates the byte-code file Maximum. class in destdir /xj /num.
To run the program, change the current directory to the destination directory destdir
and do
venusi. java xj.num . Maximum 11 12
The maximum of 11 and 12 is : 12
An integral part of Java is the Java Class library, which in Java 2 comprises more
than 1,900 classes. The Java Class Library consists of the core packages, or rho e
whose names begin with the prefix j ava, and the extension packages, or those who e
names begin with the prefix j avax. These classes are organized as a hierarchy of
packages according to their functionalities. Table 4.6 gives a brief summary of some
of the packages in Java 2.
4.6 Exceptions • 139
TABLE 4.6
Selected Packages in J2SE
Package Description
4.6 EXCEPTIONS
disrupts the normal flow of execution and provides a legitimate excu e for using goto
statements. Second, mixing the logic of error handling with the logic of regular tasks
makes a program unnecessarily large and complex and thus more difficult to read and
maintain. Finally, ad hoc methods for error handling, such as longjmp (a common
way of handling exceptions in C), are often platform specific and nonportable.
Moreover, when systems require high reliability and fail-safe processing, an
effective exception-handling mechanism is essential in the effort to deliver such assur-
ance. An exception-handling mechanism should be an integral part of a programming
language if that language is to support effectively the development of large-scale,
complex, and reliable software systems.
When an exception occurs in a Java program, the normal flow of execution is inter-
rupted, and we say that an exception has been thrown. Exceptions originate from two
sources:
1. The run-time environment (that is, the NM). Performing an illegal operation,
such as dereferencing a null pointer or accessing an array with an out-of-bound
index, causes a run-rime exception to be thrown.
2. Java programs, including the Java Class Library. When a unexpected condition is
encountered in a program, an exception can be explicitly thrown with the thro ..
statement.
A variety of exceptions are defined in the Java Class Library. Programmers may
also introduce their own exceptions.
It is important to point out that most of the run-time exceptions represent logical
errors in programs that should be fixed. Exception handling is only a mechanism
to allow recovery at run-time in the event such an error is encountered. It is not a
replacement for debugging.
9 . This diagram onl y shows some of the most common error and exception dusscs. For a complete list o f
Lhc error and exception classes, sec Lhc AP! doc umentation o f the j ava. l ang package.
4.6 Exceptions • 141
ClassCastException
lndexOutOfBoundException
Null PointerException
lllegalArgumentException
NumberFormatException
Interrupted Exception
IQ Exception
are repre ented by various ubcla e . The major categorie are error , exception•.
and run-time exceptions, which are ·ummarized in the follo\ ing table:
Category Description
Errors and run-time exceptions are called unchecked exceptions. All others are
called checked exceptions. Checked exceptions must be caught or declared in the
throws clause (see Sections 4.6.3 and 4.6.4). The following are the most common
run-time exceptions:
Most of the exceptions are thrown by the Java Virtual Machine. However, excep-
tions can also be thrown anywhere in a program to signify that an unexpected or
abnormal condition has occurred and it is impo ible to continue the normal flow of
control. Exceptions can be thrown using throw statements. The yntax of the throw
statement is
throw Exception ;
try {
(statements that may throw exceptions)
} catch (Exception! el) {
(exception handler 1 )
} catch (Exception2 e2) {
(exception handler 2)
} finally {
(finish up (optional))
}
A try-catch statement begins with a try block, which consists of the normal
flow of control. Some of the statements in the try block may throw exceptions. A
try-catch statement can have one or more catch blocks (that is, exception handlers)
and an optional finally block. Each catch block takes a single parameter, who e
type must be Throwable or one of its subclasses. Whe n an exception is thrown, the
catch blocks of the immediate enclosing try-catch statement are searched seque11tially
for a match. A catch block matches the exception thrown if the class of the exception
thrown matches the parameter type of the catch block or one of its subclasses. If a
match is found, the flow of control is transfered to the matching catch block. If a
finally block is present, it will be executed just before the flow of control leave
the try-catch statement, whether or not another exception is thrown.
Suppose that there are two catch blocks for exception classes Exc eption !
and Exc eption2, respectively, and Exception! extends Except i on2. In other
words, Exception! represents a more specific type of exceptions than Excep tion2,
and Except i on2 represents a more general type of exceptions than Ex cep tion !.
Since the catch blocks are searched sequentially, it is imporatnt to pl ace the catch
block for Except i on! before the catch block for Exception2, or the catch block
4.6 Exceptions ■ 145
for Exception! will never be reached. The catch blocks for very general types of
exceptions, such as
catch (Throwable e) or catch (Exception e)
should be avoided, since they catch almost all type of exceptions. If such indiscrim-
inatory catch blocks are needed, they should appear as the Ia t catch block.
There are several options in handling exception : (1 ) Recover from the exception
and resume execution with the statement immediately following the try-catch tate-
ment; (2) throw another exception, and pass the responsibility to another exception
handler; or (3) tenninate the program gracefully when unable to recover from the ex-
ception. In any case, the finally block, if present, will be executed before leaving
the try-catch statement.
Printing out the execution stack trace at the point where the exception occurred
in the catch block, as in the following code segment, i useful for debugging.
catch (AnException e) {
e.printStackTrace();
}
} else {
II the normal condition
total . . .
II . ..
}
II normal condition
return unitPrice * quantity;
}
II . . .
}
Now, the code for handling normal conditions is separated from the code that
handles exceptions. The logic flow under the normal condition can be written more
clearly and succinctly.
4.6 Exceptions ■ 147
PURPOSE
To illustrate the handling of exceptions in the Maximum program in Example 4.8.
DESCRIPTION
The Maximum program in Example 4.8 doe not handle the exception when the input
values are invalid. For example, invoking the program with noninteger argument will
cause a NumberFormatException to be thrown.
A more robust version of the program i to catch and handle the exception. In thi ca e.
there is no reasonable way to continue when an invalid input value i encountered. So
we cannot recover from the exception, but we can at least give a meaningful me age
in the exception handler.
SOLUTION
.
The maxfmwn of two integer arguments: Muimum2. j ava
public class Maximum2 {
public static void main (String O args) {
if (args.length >= 2) {
try {
int il = Integer.parseint (args[O] );
int i2 = Integer . parseint (args [l]);
System. out. println ( "The ma.i:imum of " + i 1 + " and " +
i2 +" is : "+ (( il >= i2) ? il : i2));
} catch (NumberFormatException e) {
System.out .println ("Invalid input value : "+
e . getMessage O) ;
System.out .println ( "The input values must be integers .");
}
} else {
System . out . println( "Usage: j ava Ma.-<:imum integer! integer2");
}
}
}
Running the program with proper argument ' produces the following re ult
Running the program with invalid arguments produces the following results:
Framework-Based Programming
Aframework provides the basic structure and utilities for applications, allowing the
application development effort to be reduced significantly. A framework is extensible
and flexible and hence can accommodate a broad range of application requirements
and functionalities. However, using a framework also means that the conventions and
styles adopted by the framework must be followed and that applications do not have
full control of the system. The top-level control of the system usually resides in the
framework, which is often called the inversion ofcontrol. Applications must cooperate
with the framework to perform their tasks.
Interaction Styles
The way in which typical Java programs interact with users can be categorized in the
following manner:
Active: Programs in this category run actively without input or intervention from
the user. The most common type of active programs consists of animation
programs, such as the applet that we present in Example 4. I 2.
Reactive: Programs in this category perform tasks in reaction to user input,
such as keystrokes, mouse clicks, and menu selections. The Drawing Pad
program discussed in Chapter 9 is an example of such a program.
Hybrid: Hybrid programs combine features from the first two categories. These
programs function by themselves and also react to user input. The Bouncing
Ball with Controls program discussed in Chapter 8 [p. 305] is an example
of a hybrid program.
--
4.7 A Simple Animation Applet • 149
PURPOSE
To illustrate the basics of the Java applet framework and simple animation.
DESCRIPTION
This applet displays the current time in hours, minutes, and seconds in the followinoe,
fonnat (Figure 4.3):
HH:MM:SS
SOLUTION
An applet does not require a main O method, but it must be a subcla s of j ava
. applet. Applet . The Applet class is actually a skeletal implementation of an
applet. An applet goes through a life cycle as illustrated in Figure 4.4. The following
methods are invoked at various points of the life cycle:
Figure 4.3
Applet Viewer: DigitalClock.class llf;J £1
The digital clock Applet
applet.
14:33:09
Applet started.
150 • Elements of Java
Figure 4.4
start
The life cycle of
applets.
Leaving Visiting destroy
1
page page
stop Discarding
page
An applet may override some of these methods. In the digital clock example, we
override three of these four methods, ini t (), start (), and stop (), and define two
other methods: paint () and run ().
All applets are graphical applications, so the graphical appearance of the applet
must be defined. One way of doing so is to use the paint () method to paint the
appearance of the applet directly. Because this is an active applet, it requires a thread
to drive the animation. One way to create a thread is to implement the Runnable
interface and define the run () method, which is the main body of the thread. The
run () method of a thread is analogous to the main () method of an application class.
We begin with the following class declaration, which shows the overall structure
of the program. We present the details of the methods separately.
The notation
indicates a placeholder of a code segment that is defined elsewhere. The page number
indicates where the code segment is defined for easy reference.
The font and color fields specify the font and color to be used in drawing the
numbers. We use a 48-point boldface monospace font and the color green. The fi eld
4. 7 A Simple Animation Applet • 151
clockThread is the thread that keeps the clock running. We discuss threads in detail
in Chapter 11 [p. 547]. However, the use of threads in all animation applets is nearly
identical and can be captured as an idiom (see Section 5.6.2).
The start () and stop () methods activate and deactivate the applet by creating
and killing the thread.
Note that the run() method, calls the repai nt () method, not pai nt O, and
the paint () method i not explicitly invoked. The mi ing link i provided by the
11
152 ■ Elements of Java
drawstring (srr, x, y )
where str is the string to be drawn and (x, y) specifies the x and y coordinates of the
left end of the string on the baseline, as illustrated in the following diagram.:
Baseline
\ Asample string.
(x, y)
7
The HTML source code for the Digi talClockDemo page follows:
<body bgcolor=white>
<hl>The Digital Clock Applet</hl><p>
<applet code=DigitalClock . class
width=250 height=80>
</applet>
<p><hr>
<a href=DigitalClock.java>The source</a>
</body>
</html>
Constant Description
new Color(r, g, b)
where ,. , g, and b are the value of the red, green, and blue components, re pectively.
They fall in the range Oto 255.
154 ■ Elements of Java
where name, style, and size are the font family name, font style, and font size, respec-
tively. If a font that exactly matches the description cannot be found, an available font
that closely matches the description will be returned. The attributes of fonts are as
follows.
Name: A string indicating the name of the font. The font name can be either
the physical font name or the logical font name. The physical font names
refer to the actual names of the fonts installed on a particular machine.
The physical font names are platform specific and often machine specific.
The logical font names refer to families of fonts with certain common
characteristics. The logical font names are mapped to physical fonts with
matching characteristics available on a given machine. The following logical
font names are available 10 :
Programs using logical font names should be able to run on any platfom1
on any machine without change. Programs using physical font names may
need to be customized to use the physical fonts available on a particular
machine.
Style: An integer indicating the style of the font. It can be one of the following
constants, defined in the Font class:
Size: A positive integer indicating the size of the font in printer's points. A point
is approximately / 2 inch on printed media and roughly one pixel on monitor
screens.
I~- For backward compalibility, the following logical fon t names are also available, but deprecated,
.
T1mesRoman, Helvetica, and Courier .
Chapter Summary ■ 155
CHAPTER. SUMMARY
■ Java supports two kinds of types: primitive types and reference types. A primitive
type variable holds a value of the type. A reference type variable holds a reference
to an object or array. Wrapper classes convert values of primitive types to objects.
■ Java supports the primitive types boolean, byte, short, int, long, char,
float, and double. Each type has default initial values for initializing variables.
■ Conversion of a type of a narrower range to a type of a broader range is caJJed
widening. Conversion of a type of a broader range to a type of a narrower range
is called narrowing.
■ Java expression and statement syntax is very similar to that of C++.
■ Objects and arrays are stored on a garbage-collected heap. They must always be
created explicitly by using the new operator, array initializer, or string literals.
■ Arrays are objects and are bound-checked.
■ Equality of references and equality of object states are two distinct concepts.
For the reference variables r1 and r2, r1 == r2 refers to the equality of the
references, and r1. equals (r2) refers to the equality of the state of the two
objects.
■ The goto statement was eliminated in Java. Multilevel breaking and the excep-
tion mechanism are used to handle situations in which goto may have been used
in other languages.
■ Classes are the basic building blocks of Java program . A Java program con i ts
of one or more class declarations. A class declaration defines a class. Each class
defines a reference type. A class declaration comprises declaration of fields,
methods, and inner classes.
• Class fields can be initialized via explicit initializers, default initial value , ini-
tialization blocks, and constructors.
• Parameters are passed by value. Values of primitive types must be wrapped inside
a class in order to serve as in-out parameters.
• Strings are objects, not char arrays. The String class i immutable, and the
StringBuff er class is mutable.
• The toStringO method of each clas is implemented to convert the instances
of the class to string repre entation .
• Interfaces declare feature but do not provide implementation. Abstract classes
are classe with partial implementation.
• Packages are used to organize large program into logical and manageable units.
A package may contain clas es. interface , or other packages.
• Exceptions are unexpected condition in programs. The exception-handling
mechanism facilitates recovery from unexpected condition or failures. A tate-
mcnt that may throw an exception mu t be placed in idea try-catch statement that
catches the exception or in a method that declares the exception in its throws
clause.
156 ■ Elements of Java
lEXERCISES
4.1 Write a Java application to calculate the 4.4 Define two Java classes:
factorial of an integer n, using iteration (not Point, with the x and y coordinates, both of
recursion). The factorial function n ! is defined which are double values, as the fields.
as Circle, with the center, an instance of Point,
0!= 1 and radius, a double value, of the circle as
n!=n*(n-1)! the fields.
Write two methods of the Circle class to do
The input value is given as a command-line
the following:
argument to the Java application.
(a) Given a point p and a circle centered at c
4.2 Write a Java application to calculate the average
of a list of integers. The input data is stored with radius r, determine whether p is inside
the circle.
in a text file, in which each line contains a
single integer. The name of the input data file (b) Given the center and radius of two circles,
is given as a command-line argument to the determine whether the two circles touch or
Java application. The output should include the overlap.
number of integers and their average. Write a main() method of the Circle class to
4.3 Write a Java application that uses the data from test each of these methods.
a text file named students. txt to create 4.5 Implement the toString () method for the
a file called studentemail . txt. Toe input Card class in Section 4.4.2. Each suit of cards
file consists of a number of lines, with each should be represented by a single letter:
line containing the data of a student in colon-
delimited format: S for • H for O D for ◊ C for eTa
Last Name: First Name: Social Security Number When the rank of a card is no higher than I 0,
it should be represented by its numeric value.
For example:
When the rank of a card is higher than 10, it
Owen:Michael :326502626 should be represented as
The output file contains student e-mail IDs J for Jack Q for Queen
generated from the information in the input K for J(jng A for Ace
file. Each input record will be converted to an
e-mail ID in the following format: For example, the • King should be represented
as SK.
the first character of the first name +
4.6 Implement a shuffle() method for the Deck
the first character of the last name +
class in Section 4.4.2 to shufiie the deck of
the last 4 digits of the social security number+
"O" + cards. Use Math. random() to emulate the
"se . depaul . edu" randomness of shuffling. Then implement a
deal () method to deal the cards in the shuffled
For example, Michael Owen's new e-mail ID deck to four hands, and use the toString 0
would be method in Exercise 4.5 to print out the hands.
mo26260se.depaul.edu 4.7 Using packages.
(a) Move each of the Java application in
Note that no uppercase letters are allowed in the Exercises 4.1 -4.3 to a package named
new e-mail IDs and that the output file should myprog. single. Compile and run each
contain one e-mail ID per line. of the applications.
Project • 15 7
(b) Move bolh Point and Circle classes The scores of the project and exams are
in Exercise 4.4 lo a package named recorded as integers between 0 and l 00, as
myprog. multi. Compile and run the ap- in the record
plication.
Johnson:Phil:100:90:95 :84:91
4.8 Run the Java application in Exercise 4.2 with
invalid input: The total score of each student i calculated by
(a) Provide an incorrect file name; that is, the u ing the formula
file named does nol exist. Total = (Project I) x 10% + (Project 2) x 10% +
(b) Provide a correct file name, but the input
(Project 3) x 10% + (Midterm exam) x 30% +
file contains invalid contents; for example,
a line contains a noninteger or multiple (Final exam) x 40%
integers. The total core is then converted to a letter grade
Write an improved version of the program using by using the chart
exception handling. This program should give
Total 2:: 90 A
a meaningful message when the input file name
is incorrect, and ignore any lines in the input 90 > Total 2:: 80 B
file that are other than a single integer. 80 > Total 2:: 70 C
4.9 The Calendar class also provides methods to 70 > Total 2:: 60 D
get the current year, month, date, and day of Total < 60 F
the week. Enhance the digital clock applet to
do the following: Write a Java application that wilJ read the
student record file from tandard input and
(a) Display the current year, month, date, and
do the following:
day of week.
(b) Display the time using the 12-hour formal (a) Calculate the total score and the letter grade
instead of the 24-hour format. for each tudent. Print the re ults to tandard
output in the format
For example, the display might be
last name first name total grade
3:05:28 PM
5 May 2002 Tue (b) Calculate the highe t. !owe l. and average
core of the rudent project . exam . and
total core . and print the re ult. to tandard
output.
(c) Count the number of tudcnt in each letter-
grade range, and print the count to standard
4.1 A ssume that you have a tudent record file. output.
It is a text file in which each line contains a A ume that the input file contain. no more
s ingle record, and each record consist of the than I00 record .
following colon-delimited fields :
........... ,
I ,
....
CHAPTER OVERVIEW
In this chapter, we d iscuss overloading, inheritance, overriding, hiding, subtypes, poly-
morphism, and casting. In addition, we examine issues involved in designing and im-
plementing classes and offer some design guidelines. We also present several more
sophisticated animation applets to illustrate techniques and idioms commonly used in
animation.
159
160 • Classes and Inheritance
Method Signature
The condition under which overloading is allowed is stated as the following rule:
/.. calculate the distance between this point and the other point ·t
public double distance(Point other) {
double dx = this.x - other.x ;
double dy = t his.y - other . y;
return Math.sqrt (dx * dx + dy * dy);
}
5.1 Overloading Methods and Constructors • 161
II other methods
}
When an overloaded method i called, the number and the type of the arguments
~e used to determine the signature of the method that will be invoked. Overloading
1s resolved at compile time, as in the following code segment:
Operator Overloading
Operator overloading is a generalization of overloading of method . lt allow. oper-
ators, such a +, *, and ==. etc., to operate on different type of parameter , i.e.,
operands. In Java, operator are overloaded only on built-in operation on primitive
types. For example, the arithmetic operators (+.-, *, / ,%) are overloaded on all nu-
meric type . The only exception is that the operator + and += are al o o erloaded on
class String for concatenation. No other cla e · are allO\ ed to overload operators.
Operator overloading i, an important charncteri 'tic of C++. When u ed properly,
it allows computations to be expre ed naturally and ·uccinctly while maintaining the
conventional meaning of the operator. Operator · hould be defined in accordance with
their usual and conventional meanings. Howewr, there is nothing to prevent operators
from being overloaded in m.i'leading \ a ·. Furthermore, as we learned from C++,
overuse of operator overloading can hamper the readability of programs. Thu the
potential pitfall ' of operator overloading over ·hadow any possible benefits. For thi
reason, Java restri ts operator overloading to its primitive data types and strings.
162 ■ Classes and Inheritance
In the first case, all the overloaded methods have the same number of parameters
but with different types. An example is the overloaded append () methods in the class
StringBuffer.
A string representation of the argument is appended to the end of the current contents
of the string buffer.
Another example in this category is the overloaded max () method in the class Math.
Again, all the overloaded methods fit the following general description:
In the second case, the overloaded methods have different numbers of panunete
Usually the one with the most parameters is the base method, which implements t~s.
actual functionality. All other overloaded methods simply delegate the task to the bas:
method. An example is the overloaded substring method in the class String.
public class String {
public String substring (int i, int j) {
II base method: return substring [i .. j - 1]
}
public String substring (int i) {
II provide default argument
return substring (i , length ());
}
II . . .
}
Both methods provide the same functionality: They return a substring. The method
with two parameters is the base method. The method with a single parameter simply
delegates the task to the base method by supplying a default second argument.
Overloading should be avoided in all other situations.
Inheritance defines a relationship among clas es. When cla C2 inherits from, or
extends , class Cl, class C2 is called a subclass or an extended class of class Cl, and
class Cl is called a superclass of C2. Inheritance is a mechanism for reusing the
implementation and extending the functionalities of superclasses. All the public and •
protected members of the superclass are accessible in the extended classes.•
The extension relationship among classe is the strict form of inheritance, in
the sense that the implementation of the superclass is actually inherited or reu ed in
the ubclasses. Two other relationships can be viewed as weak form of inheritance:
interface extension and interface implementation ( ee Section 5.3 [p. 176]). Java
supports only single inheritance for class exten ion; that i , each cla may not have
more than one superclass. The exten ion relation among cla se form a hierarchy
with the class Obj ect a it root. Every cla other than Object ha. a unique
superclass. If no supercla s i explicitly declared. Object i a urned to be the
superclass. The Object class i the only cla with no upercla . Multiple inheritance
is supported for interface exten ion and implementation. We di cu the ex ten ion aod
implementation of interface in Section 5.3 [p. 176].
Again, the syntax of cla declaration i a hown in the follow ing fragment. The
extends clause specifie the uperclass, and the implements clau e specifies the
interfaces implemented by the cla .
[ClassModifiers] class ClassName
[ extends SuperC/ass]
[implements /11terface 1 , /11te,face~ . .. I {
ClassMe111berDec/amrio11s
}
164 ■ Classes and Inheritance
import java.awt.Color;
public class ColoredPoint extends Point {
public Color color;
public ColoredPoint(final double x, final double y,
final Color color) {
super(x, y);
this.color= color;
}
public ColoredPoint() {
color= Color.black;
}
}
The fields inherited from the superclass, x and y, should be initialized by invoking
one of the constructors of the Point class. The new field in the extended class, color,
should be initialized in the constructors of the ColoredPoint class. The constructors
of the superclass are invoked with the keyword super.
• In the first constructor of the ColoredPoint class, super (x, y) invokes the
constructor of the Point class with a matching signature: Point (double,
double) . The invocation of super (. . . ) must be the first statement in the
constructor of the extended class.
• The second constructor of the ColoredPoint class supplies a default value
for the color field. It invokes another constructor of the same class with the
keyword this. The constructor invoked is the one with the matching signature:
ColoredPoint (double, double, Color) . The invocation of this (. . .
) must be the first statement in the constructor.
• In the third constructor of the ColoredPoint class, no explicit invocation using
~uper or this is made. ln this case, the no-arg constructor of the superclass i
invoked implicitly.
of the supercla s. For example, the following no-arg constructor will be provided im-
plicitly for the Extended cla s when no constructor is provided explicitly:
public class ExtendedClass extends SuperClass {
public ExtendedClass() {
super();
}
II methods and fields
}
lf the Superclass does not provide a no-arg constructor, a compilation error will
result. If another constructor of the ExtendedClass is present, the no-arg constructor
is not provided implicitly.
The extended class may also u e explicit initializers or the default initial values
to initialize the fields. The following order of initialization applies to the fields in both
the superclass and the extended class:
1. The fields of the superclass are initialized, using explicit initializer or the default
initial values.
2. One of the constructor of the uperclass is executed.
3. The fields of the extended class are initialized, using field initializer or the default
initial values.
4. One of the con tructor of the extended class i executed.
For example, let us consider the classe Super and Extended.
public class SuperClass {
int x = . . . ; II executed first
public Superclass () {
x = . . . ; II executed second
}
II . ..
}
public class ExtendedClass extends Superclass {
int y = . . . ; II executed third
public ExtendedClass () {
super();
y = . . . ; II executed fourth
}
II . ..
}
Subtypes
The inheritance relationship can be viewed in various ways, as illustrated in Fig-
ure 5. l(a). A subclass extends the capability of its superclass; the subclass inherits
features from its superclass and adds more features . Moreover, a subclass is a special-
ization of its superclass; every instance of a subclass is an instance of the superclass,
not vice versa. For example, every line is a shape but not every shape is a line. Some
features are available in the subclass but not in the superclass. There is yet another
view of classes and inheritance: Each class defines a type. AH the instances of the
class constitute the set of the legitimate values of that type. A s every instance of a
subclass is also an instance of its superclass, the type defined by the subclass is a
proper subset of the type defined by its superclass. The set of all the instances of a
subclass is included in the set of all the instances of its superclass, as shown in Fig-
ure 5.1 (b). The subset relation between the value set of types is known as the subtype
relation.
Figure 5.1
Different views
of inheritance:
(a) Extending the
Line
Shape
Rectangle
e Shapes
--~
superclass; and
(b) inclusion of (a)
instance sets. (b)
--
5.2 Extending Classes • 16 7
Substitutability of Subtypes
A value of a subtype can appear wherever a value of its supertype i expected. In other
words, a value of a subtype can always substitute for a value of its supertype.
To rephrase this rule in the context of classe and object : An in tance of a ubclas can
appear wherever an instance of its superclass is expected. An instance of a ubclas
can always substitute for an instance of its superclass.
The widening of reference types i alwar allowed and i carried out implicitly
whenever necessary. In other word , a reference to an object of cla . C can be
implicitly converted to a reference to an object of one of the _upercla_ e of C. The
narrowing of reference type require explicit cast. Thu , the narrm ing of reference
types is also known as dow11casri11g. Narro\ ing i · alwa allowed at compile time.
However, it i not always safe, and it may result in run-time exception .
There i also an important difference bet\ een the con er ion of primitive types
and reference types. The con er·ion of primitive type re. ult in a change of the
repre entation of the value · being converted. F r example, con erting an int to
double results in representing the int value in double format. The conver ion of
an objec t reference does 1101 affect the repre entation of the object. Its identity and
tah.: remain the same.
168 • Classes and Inheritance
Polymorphic Assignment
In static programming languages, such as C, the rule of assignments is that the left-
hand side and the right-hand side of an assignment must be of compatible types. In
object-oriented languages a more powerful form of assignment, known as polymor-
phic assignment, is allowed.
Rule of Assignment
The type of the expression at the right-hand side of an assignment must be a subtype
of the type of the variable at the left-hand side of the assignment.
This rule means that the variable on the left-hand side may hold objects of
different classes. In other words, if class E extends class B, any instance of E can
act as an instance of B. In the following code fragment the Student class has two
subclasses, Undergraduate and Graduate:
class Student { . . . }
class Undergraduate extends Student { }
class Graduate extends Student { . . . }
In the assignments shown, the types of the expressions on the right-hand side f
are Undergraduate and Graduate, respectively. They are subtypes of the type of
the variable on the left-hand side, Student. Therefore, no explicit cast is necessary.
However, attempting to do the following will result in a compilation error.
Graduate student3;
student3 = student2; // compilation error
An error will be generated because the right-hand side type is Student and the
left-hand side type is Graduate. Even though student2 actually holds a reference to
an instance of Graduate, the declared type of student2 is Student , which is not a
subtype of the left-hand side type Graduate. Type checking is carried out at compile
time and is based on the declared types of variables. An explicit cast is necessary here:
student3 • (Graduate) student2; // explicit cast, okay
Casting a reference variable to a subtype of its declared type is called down cast-
ing. J~va _allows expli~i~ casting of any reference type to any other reference type at
compile lime. The vahd1ty of an explicit cast is always checked at t' If h t
· · I'd run 11ne. t e ca
1s mva 1 , a ClassCastExcepti on will be thrown.
5.2 Extending Classes • 169
This statement will not result in a compilation error. However, as student 1 actually
holds an instance of Undergraduate, which is not a subtype of Graduate, a
ClassCastException will be thrown at run time.
Since downcasting may result in exceptions being thrown, it should be done with
proper care. There are two proper ways of downcasting:
1. The pessimistic approach. Use the instanceof operator to check the cla of
an object before downcasting. The expression
try {
II . . .
Graduate gradStudent = (Graduate) student!;
II . ..
} catch (ClassCastException e) {
II student1 is not a graduate student
}
Failing to downcast in eitherof these way may lead to run-time failure of the program.
... __....,..___________________________
170 ■ Classes and Inheritance
Then the question is, Why not declare student to be Graduate in the first
place? The possible reasons are as follows :
Array Types
Java arrays are objects. The subtype relationships among array types are defined as
follows. First, all array types are subtypes of Object (e.g., int[] and double[] are
subtypes of Object). Second, if class or interface Y is a subtype of class or interface
type X, then Y[] is also a subtype of X[] .
The following diagram illustrates the subtype relation among array types:
Object
int[] double X
y Y[]
---
5.2 Extending Classes ■ 171
However,
Graduate student6 = sal [OJ ; II compilation error
class Acontains two overloaded method . An in tance of Acan acce s both methods,
depending on the arguments of the method 'invocation, o
Aa =newA() ;
a. ml O; II Invoke m1 ()
a . ml (1) ; II Invoke m1 (int)
172 • Classes and Inheritance
class B {
public void m20 { . . . }
}
class C extends B {
public void m2 0 {. . . }
}
B b = new BO;
Cc= new C();
b . m2 () ; II invoke the m2( ) in class B
c . m2 () ; II invoke the m2() in class C
Figure 5.2 shows the relationship between the classes. Note that the assignment
in enroll,
students[count++] = s;
students[i] .toString()
The output is
Undergraduate student : John
Graduate student: Mark
Undergraduate student: Jane
Note that the expression students [i] . toString () was executed three times.
Twice it was bound to the implementation in the Undergraduate class, and once
it was bound to the implementation in the Graduate class.
Final Methods
A method that is declared as final cannot be overridden in a subclass. Final methods
are useful in preventing a subclass from accidentally overriding methods that should
. not be overridden. Usually, such methods collaborate with some other methods or
objects and are expected to honor certain conventions and contracts. Accidental
overriding of these methods could violate the contracts and cause program failures.
Final methods also allow the Java compiler and JVM to optimize byte-code.
Figure 5.2
Student Course
Students and Student [] students
courses. int count
Undergraduate Graduate static final int CAPACITY
void enroll(Student s)
void list()
5.2 Extending Classes • 175
II other declarations
}
Now, we try to define the method equals() for the class ColoredPoint,
which extends the class Point. The equals() of ColoredPoint overrides the
equals() of Point. However, we do not want to repeat implementation of the
method equals() of Point. We want to use the method equals () of Point
to check the equality of the inherited fields and check only the equality of the
fields declared in ColoredPoint in the equals() method of ColoredPoint. The
equals () method of Point can be invoked via the super reference.
public class ColoredPoint extends Point {
public boolean equals(Object other) {
if (other != null &&
other instanceof ColoredPoint ) {
ColoredPoint p = (ColoredPoint ) other ;
return (super . equals (p) && color .equals (p.color));
} else {
return false;
}
}
II other declarations
}
The keyword super represents an object reference having the same value a. this,
but it behaves as if it were a reference to the superclass.
Restriction
In the following scenario, we have a Polygon clas :
public class Polygon {
public void move (int dx, int dy) { . , }
public void scale (double fact or ) { . . }
public void addVertex (Poi nt p) { }
}
176 ■ Classes and Inheritance
In the latter case, the throws clause must be included in the method declaration of
the superclass. (See the discussion of the throws clause in Section 4.6 [p. 139].)
interf~ces, and an int~rfac~ may e~tend multiple interfaces. Although the Object
class 1s the root of the mhentance hierarchy among classes, there is no single root for
the interface extension and implementation relationships.
A class that implements an interface provides implementation for the abstract
methods declared in the interface by overriding those methods, as illustrated by the
following program fragment:
public interface Myinterface {
void aMethod(int i); II an abstract method
}
public class MyClass implements Myinterface {
public void aMethod (int i) {
II implementation
}
II . ..
}
Each interface also defines a type. The interface extension and implementation are
also subtype relations. A reference type in Java can be either a class type, an array
type, or an interface type. Now, we can define the complete subtype relations among
reference types.
Figure 5.3
Implementation of
Interfaces.
5.3 Extending and Implementing Interfaces ■ 179
Therefore, a student employee can play two different roles in two different
contexts. This dichotomy is the key advantage for allowing a class to have multiple
supertypes via interface implementation.
Whether to support single or multiple inheritance is one of the most hotly debated
issues in object-oriented programming language design. C++ supports true multiple
inheritance, whereas Java supports only a limited form of multiple inheritance through
interface extension and implementation. In the preceding example, the StudentEm-
ployee class implements two interfaces but inherits no implementation from the
interfaces, as interfaces have no implementation. As a consequence, the getGPA 0
method is implemented separately in the FulltimeStudent and the StudentEm-
ployee classes. Similarly, the getSalary ( ) method is implemented separately in
the FulltimeEmployee and the StudentEmployee classes. With the type of true
multiple inheritance supported by C++, besides the subtype relationship that allows
the instances of a subclass to play different roles, a subclass can also inherit (i.e.,
reuse) implementation from multiple superclasses. For example, with true multiple
inheritance, we could do the following:
public class Student {
public float getGPA () {
II calculate GPA
}
protected float gpa ;
II . . . other methods and fields
}
Now, the question is, How many names does a student employee have? Both the
Student and Employee classes inherit a copy of the name field from the Person
5.3 Extending and Implementing Interfaces • 181
Figure 5.4
Diamond-shaped
multiple inheri-
tance relationship. Student Employee
StudentEmployee
Figure 5.5
I Stu$ent I Employee
'i-
Implementation I i
reuse through
1----- -- --'- ------ I I - - - - - - _ .._ - - - - - - - -
I I
I ,------'---~
delegation. Studentlmpl I
I Employeelmpl
class, and the StudentEmployee class in turn inherits all the fields from both of its
superclasses. Therefore, each instance of the StudentEmployee class contains two
copies of the name field. When we try to access the name field of a student employee,
which copy is accessed? Is it possible to access both copies? Common ense tells us
that there should be only one copy of the name field instead of two for each student
employee. These issues must be addressed when we use true multiple inheritance in
C++, whose semantics are further complicated by name re olution features and virtual
base classes. The complicated semantics of C++ make the implementation and u e of
multiple inheritance difficult.
The restricted form of multiple inheritance in Java is much simpler. Furthermore.
the reuse of implementation can be accomplished by using an alternative mechanism,
as shown in Figure 5.5. Assume that we have two interfaces:
interface Student {
public float getGPA ();
}
interface Employee {
public float getSalary();
}
The implementation in Student Impl and Employee Impl can be directly reused
in the full-time student and employee classes by utilizing class extension:
The implementation technique used in the get GPA() and get Salary() meth-
ods is known as delegation because each method simply delegates the task to another
object, studentimpl and employeeimpl, respectively. The implementation in the
Studentimpl and Employeelmpl classes is reused through delegation.
The simple and restricted form of multiple inheritance adopted in Java coupled
with the delegation technique can accomplish everything that can be accomplished
by using true multiple inheritance.
5.3 Extending and Implementing Interfaces ■ 183
In Java, a class may extend one class and implement multiple interfaces, and an
interface may extend multiple interfaces. Names inherited from one interface may
collide with names inherited from another interface or class. However, name collisions
are fairly easy to resolve in Java.
Two methods that have the same name offer the following possibilities:
■ If they have different signatures, they are considered to be overloaded.
■ If they have the same signature and the same return type, they are considered to
be the same method. In other words, the two methods collapse into one.
■ If they have the same signature but different return types, a compilation error will
result.
■ If they have the same signature and the same return type but throw different
exceptions, they are considered to be the same method, and the resulting throws
list is the union of the two original throws lists.
For example, let us consider two interfaces and a class that implements both.
public interface X {
public void methodl(int i) ;
public void method2 (int i);
public void method3 (int i);
public void method4(int i) throws Exceptionl;
}
public interfaces Y {
public void methodl(double d);
public void method2 (int i);
public int method3 (int i);
public void method4 (int i) throws Exception2 ;
}
The two methods named method1 () in MyClass are overloaded. The two
methods named method3 () in X and Y will cau e a compilation error.
Two constants having the ame name i alway allowed. They are con idered to
be two separate constants, a in
public interface X {
static final int a =
}
public interfaces Y {
static final double a a • ••
}
184 ■ Classes and Inheritance
Marker interfaces are empty interfaces, that is, interfaces that declare no methods or
constants. They establish a subtype relationship between themselves and the classes
that implement them. They are intended to mark classes as having certain properties.
The most commonly used marker interface is the Cloneable interface. The Clone-
able interface is used to distinguish classes that can be cloned from those that cannot
be cloned. Only those classes that implement the Clone able interface can be cloned.
When a subclass declares a field or class method that is already declared in its
superclass, the field or class method in the superclass is not overridden; it is hidden.
The instance field className of the ColoredPoint class hides the field of the same
name of the Point class. The static method getDescription () of the Colored-
Point class hides the method of the same name of the Point class.
The output is
ColoredPoint
Point
ColoredPoint
Point
Although, both pl and p2 refer to the ame object, the binding of ~e s~atic
methods and fields is based on the declared types of the variables at compile ume.
The declared type of pl and p2 are ColoredPoint and Point, respectively. Hence
the result. 'd hid' Hid
If the preceding example look confu ing, that is the reason to a:~ 1 mg. -
ing field and tatic method offer little help but hampers the reada~i~ty of pro~rams.
The mies of hiding are intended to resolve coincidental nan1e colhs1ons, that 1s, un-
related features that happen to have the an1e name in subclasses and superclasses.
186 • Classes and Inheritance
we should write
System.out.println(ColoredPoint.getDescription());
System.out.println(Point.getDescription());
APPLICATIONS-ANIMATION APPLETS
In this section, we present some animation applets to illustrate the graphics capability
of Java and important techniques used in animation.
The initial version of the Digital Clock applet is simple but not very flexible. Nothing
can be changed without modifying and recompiling the source code. The flexibility to
change the appearance or behavior of applets is desirable in many situations. Applets
provide this type of flexibility via the parameters supplied in the applet tag.
PURPOSE
To illustrate the use of parameters in applets and enhancing a class by extension.
DESCRIPTION
In this example, we develop a digital clock that behaves the same as the original
version (see Example 4.12) but allows the foreground color to be set as a parameter.
5.5 Applications-Animation Applets ■ 187
SOLUTION
Because• •
we intend
.
to preserve
•
the behavior of the on·c,o 1·nal di'g•tal
1 cIock , we extend
the or:igmal.vers10_n t? obtam the enhanced version. The only method that needs to be
ovemdden 1s the 1n1 t () method, in which the display color is set.
,......,~dock~:DigitalCloct2.java
import java . awt . Color ;
public class Digita1Clock2 extends DigitalClock {
public void init () {
String param • getParameter("color");
i f ("red" . equal s (par am)) {
color= Color . red;
} else if ("blue 11 .equal s (param) ) {
color= Color . blue ;
} else i f ( 11 yellow" . equal s (par am)) {
color= Color .yell ow ;
} else i f ( 11 orange " . equals (par am)) {
color= Col or . orange ;
} else {
color= Color . green;
}
}
}
In general, the applet tag with parameters taJce the following form:
<applet code•class_jile11ame
width•pixe/s height•pixe/s>
<param name•param_name1 value•param_val11ei>
PURPOSE
To illustrate animation and text drawing.
DESCRIPTION
In this example, we develop a simple animation applet. It displays a text banner that
moves horizontally from right to left. When the banner moves completely off the left
end of the viewing area, it reappears at the right end (see Figure 5.6).
SOLUTION
The fields of the ScrollingBanner class are summarized in the following table.
Field Description
Figure 5.6
■44·1 @ 11t4Vi§►i9Nill&i;ki,iU§ft46ti1W-□1tx1
The scrolling Applet
banner applet.
Java is cool
Applet started.
5.5 Applications-Animation Applets • 189
The ini t () method handles the initialization of the applet. It retrieves two
parameters: delay and text. The get Size() method returns the size of the viewing
area in the Web page. The result is of type Dimension, which is declared as
The initial position of the text is set at the rightmost position in the viewing area.
The geometry of the drawing is illustrated in Figure 5. 7.
The paint () method paints the current frame. The key to the animation is that
the position of the text has to be adjusted from the previous frame. If the text fall s out
of bounds, it should be repositioned.
The control portion of the applet, which consists of the bannerThread variable
and the start O , stop() , and run() methods, is nearly identical to that of the
digital clock applet.
The generic structure of the top-level class of an animation applet can be de-
scribed as follows:
public class AnimationApplet
extends java . applet . Applet implements Runnable {
Thread mainThread = null;
int delay;
public void start(){
if (mainThread == null) {
mainThread = new Thread(this) ;
mainThread . start O ;
}
}
public void stop() {
mainThread = null ;
}
public void run() {
while (Thread. currentThread() == mainThread) {
repaint();
try {
Thread.currentThread().sleep(delay);
}
catch (InterruptedException e) {}
}
}
public void paint(java.awt . Graphics g) {
(paint the current frame)
}
When using this idiom to create an animation applet, you should copy the code
shown in Courier font verbatim, but you can replace the names shown in italic with
any other names. Field main Thread is the main control thread. Field delay determines
the refresh rate.
..
The java.avt.FontMetrics Class
The FontMetrics class provides the metrics for each font design. The most com-
monly used metrics are those illustrated in the following diagram, along with the
methods for retrieving these metrics:
Method Description
It may seem natural for the Font class to provide a method, ay, getFontMet-
rics () , to return an instance of the FontMetrics class corresponding to a given
font. However, the getFontMetri cs O method is not provided by the Font class,
but by the Graphics class instead. The reason is that the metrics of a fon t depend not
only on the font family and the size and style of the font, but also on the re olution of
the graphics context in which the characters will be rendered. Therefore, the metrics
of a font should be obtained as follows, assuming that g is an instance of Gr aphi cs:
The ScrollingBanner has a minor glitch. If you pay clo e attention, you will notice
that it flickers. To understand the cause of the flickering, we need to look more clo ely
at how frames are painted. The animation process is controlled by the whil e loop in
the run method. During each iteration, the thread sleeps for a hart period of time
before calling the repaint() method. The repaint() method call another method:
update O. The default implementation of the updat e O method
■ clears the background by filling it with the background color (u ually, the default
background color is gray or white),
■ sets the pen color to the foreground color, and
■ calls the pai nt () method.
In the pai nt ( ) method, the background i repainted- this time in black- and then
the text is drawn. Because the painting i done directly on the screen, it cannot be
completed instantaneou ly. The background painting may take long enough for the
human eye to notice; hence the flickering effect.
194 ■ Classes and Inheritance
PURPOSE
To illustrate double-buffered animation and enhancing a class by extension.
DESCRIPTION
This version of the scrolling banner applet behaves basically the same as the initial
version but eliminates the flickering.
SOLUTION
The solution to avoid flickering is quite simple. Instead of painting each frame directly
on the screen, we first paint it in a temporary buffer in memory. When the frame has
been completed, we copy it from the temporary buffer to the screen in a single step.
The painting in progress is not visible because it is done in a temporary buffer. Copying
an image from memory to the screen is usually supported by graphics hardware so that
it can be done fast, resulting in smoother motion from frame to frame. This technique
is commonly called double-buffering, or off-screen drawing.
Now, we try to improve the scrolling banner applet by using double-buffering.
The main functionality of the moving text remains the same, so we simply need to
extend the original applet.
Field Description
image An off-screen image that is the same size as the viewing area
off screen A graphics context associated with the off-screen image
PURPOSE
To illustrate the Animation Applet Idiom, graphics drawing, and double-buffering.
DESCRIPTION
Our new animation applet shows a ball moving inside a rectangular box (see Fig-
ure 5.8). The ball reverses direction when it touches any of the four sides of the box.
SOLUTION
The fields of the BouncingBall class are as follows:
Field Description
Figure 5.8
'--...
~- ~P , ~
~l.:) ~\·
-~ "'v ~\Y
0/ "\.
u
~ _j J i111port java.awt.•; , &-C '\
~ ...) public class BouncingBall ~
3 <' ~ extends java.applet.Applet implements Runnable {
~ !°)· protected Color color = Color. green;
protected int radius= 20;
protected int x, y; . ~ ~
protected int dx = -2, dy = --4; ➔ ~ ,~
~rotected Image image; ~ ~
protected Graphics offscreen;1:.......... ---x.,_~ ..s.5f'~
0
protected Dimension d;
..-_ public void initO {
String att • getParameter("delay");
if (att !• null) {
delay• Integer .parseint(att);
}
d • getSize () ;
x • d.vidth * 2 / 3 ;
y • d.height - radius;
}
}
}
•
The j ava. awt. Graphics Class
The Graphics class is an abstraction of different displaying devices, such as screens,
printers, and off-screen images. It encapsulates the details and characteristics of these
devices and allows us to treat them unifonnly.
198 • Classes and Inheritance
The Graphics class is fairly large and provides the basic capabilities for two-
dimensional graphics. More sophisticated two-dimensional graphics capabilities are
provided in a subclass Graphics2D.
Two groups of methods are commonly used in this class: methods for setting and
retrieving attributes and methods for drawing. The methods for setting and retrieving
attributes are summarized in the following table. In the parameters of the methods,
color is an instance of the class Color and font is an intance of the class Font.
Method Description
Drawing methods are divided into two categories: (1) methods that have a draw
prefix, which draw the outlines of certain shapes with the current color; and (2) meth-
ods that have a fill prefix, which fill the interior areas of certain shapes with the
current color. The methods for drawing various shapes are summarized as follows.
Text String
Baseline
dravString(String s, int x, int y)
\?Xy
(x, y)
The ordered pair (x, y) specifies the left end of the string on the baseline.
Line Segment
void dravLine(int xl, int yl,
int x2, int y2)
The ordered pairs (x1, Y1) and (x2, Y2) specify the two ends of the line segment.
5.5 Applications-Animation Applets ■ 199
Rectangle
(x, y)
void drawRect(int x, int y, int w, int h)
void fillRect(int x, int y, int w, int h)
DF w
The ordered pair (x, y) specifies the upper-left comer of the rectangle, and wand h
specify the width and height, respectively, of the rectangle.
Oval (x, y)
void draw□val(int x, int y, int w, int h)
void fill □val(int x, int y, int w, int h)
=□
.
• ····· ··· •·rh
. . . -~ .....
w
The ordered pair (x, y) specifies the upper-left comer of the surrounding rectangle;
and w and h specify the width and height, respectively, of the surrounding rectangle.
Round-Cornered Rectangle
(x, y)
void drawRoundRect(int x, int y, int w,
int h, int rw, int rh)
void fillRoundRect(int x, int y, int w,
{
rh
int h, int rw, int rh)
w
The ordered pair (x, y) specifies the upper-left corner of the surrounding rectangle;
w and h specify the width and the height, respectively, of the surrounding rectangle;
and rw and rh specify the width and height, respectively, of the quarter ovals at the
comers.
□}
boolean raised)
void fill3DRect(int x, int y, int w, int h,
boolean raised)
w
The ordered pair (x, y) specifies the upper-left comer of the rectangle; and w and h
specify the width and height, respectively, of the rectangle. The boolean parameter
raised has the following effects:
Arc
void drawArc(int x, int y, int w, int h,
int rs, intra)
void fillArc(int x, int y, int w, int h,
int rs, intra)
The ordered pair (x, y) specifies the upper-left corner of the surrounding rectangle;
w and h specify the width and height, respectively, of the surrounding rectangle; and
rs and ra specify the starting angle and the angle covered by the arc, respectively, in
degrees.
An applet is not allowed to read or write files on the client machine-the machine
on which the applet is executed. However, an applet is allowed to read files from the
server host-the host from which the applet is downloaded.
The URL
The URL class represents the Universal Resource Locator (URL). A URL talces the
following form. 1
http : //java.sun.com:80/document/index.html#section2
Protocol http: / /
Host address j ava. sun. com
Port :80
Path /document/index.html
Section reference #section2
_I. The dou_ble slash (If) in a ~eb site address does not indicate a comment as it does in program code. When
1thappe~ s m program code, ll must be enclosed by double quotes, as in: "http:// j ava. sun . com/ index
. tm1 .
5.5 Applications-Animation Applets ■ 201
URLs can be constructed by using one of the following constructors of the URL
class.
For example,
URL urll = new URL ( "http : //j ava. sun . com/index.html" ) ;
II Construct URL: http:l{java.sun.com/index.html
URL url2 = new URL (urll, "document/intro . html");
II Construct URL: http:/{java.sun.com/document/intro.html
The following two methods of the Applet class return the URL of the document
page and the Java class file , respectively:
Method Description
getDocumentBase () Return the URL of the Web page in which the applet is
emdedded.
getCodeBase () Return the URL of the applet class file.
An applet can read a file by first constructing a URL, u ing the relative path of
the file to be read. A text file named filename can be read a follow :
URL url = new URL (getDocumentBase () , filename) ;
BufferedReader in=
new BufferedReader (new I nputStreamReader (
url .openStream ()));
String line;
try {
while ((line= in.readLine () ) != null ) {
II process line
}
} catch (IOException e) {}
We di cu s the BufferedReader and InputStreamReader classes in Sec-
tion 8.4 [p. 366].
202 ■ Classes and Inheritance
Method Description
Applets run fine with The most likely cause is that all the byte-
the applet viewer code files (. class files) were not transfered
but do not run in a to the Web server.
browser.
5.1 A polymorphic array of shapes. (iii) Print the string representations of all the
shapes stored in the array to standard
(a) Implement a simple class hierarchy of
output.
shapes, which consists of an interface.
Assume that the input file contains no more
than 100 shapes.
All numbers are integer literals. Fields 5.1 Write an applet that retrieves a text file contain-
are delimited by white spaces.
ing a list of points, and produce a scatterplot
(ii) Create instances of the shapes de- in a two-dimensional coordinate system. Each
scribed in the input file and store them line of the input file contains a single point,
in an array of Shapes. and each point is represented by two integers
Projects • 205
that specify the x and y coordinates and are 5.3 Write an applet that retrieves sales data as
separated by white spaces. parameters in the same format as in Project 5.2,
5.2 Write an applet that retrieves sales data as and display the data using a bar chart.
parameters, and display the data on a pie chart. 5.4 Extend Exercise 5.1 by adding a draw()
The format of the parameters is the following: method to each of the shape classes that draws
the respective shape. Write an applet that reads
<par am name=categories value="cat 1 • • • cat0 "> a file that contains descriptions of different
<param name=cat 1 value=amount 1> shapes, and draw the shapes using the draw ()
method.
<param name=cat 0 value=amount 0 >
<param name=categories
value="education utility
entertainment reference"
<param name=education value=10000>
<param name=utility value=12000>
<param name=entertainment value=30000>
<param name=reference value=9000>
From Building Blocks to Projects
CHAPTER OVERVIEW
In this chapter, we examine the issues involved in designing classes: organizing files and
classes, and using the canonical form of public classes. We present a number of important
design guidelines. We also briefly discuss the documentation of classes and methods and
the use of the j avadoc utility. We introduce the concepts of contracts, preconditions,
postconditions, and invariants, and using the assertion facility to check preconditions,
postconditions, and invariants at run time. Finally, we briefly discuss two useful tools:
JUnit for unit testing of classes and Ant for project building.
.... ,.
208 • From Building Blocks to Projects
it should then reside in a separate file. It should reside in the same file as the class it
supports. If an auxiliary class supports a multiple public class in the same package, it
may reside in a separate file.
Throughout this chapter, we use an interface List as shown in Figure 6.1 and a
class LinkedList, which implements the List interface using a doubly linked list,
as examples. 1 Now, let us first consider the organization of the LinkedList . j ava
class. The LinkedList. j ava class is a public class and resides in a file named
LinkedList . j ava. Since the implementation uses a doubly linked list, the
LinkedList class requires an auxiliary class Node, which represents the nodes in
the doubly linked lisL The Node class is used only in implementation and should not
be exposed to clients. The Node class should not be public and should reside in the
same file as the LinkedList class (i.e., the LinkedList. j ava file). There are two
options for the Node class:
2. The Node class can be a nested class of the LinkedList class. In file Linked
List . java,
The difference between the two is that in option 1 the Node class is not accessible
to any subclass of LinkedList not in the same package, whereas in option 2 the Node
class is accessible to subclasses of LinkedList.
In Java, the order of the class members is insignificant. In other words, reordering
the members of a class does not alter the semantics of the class. This characteristic
allows the members of classes to be ordered to maximize the readability.
The fields and methods of a class should be ordered according to their accessi-
bility and roles. The methods of a class should be organized into groups appearing in
the following order:
■ Public constructors
■ Public accessors, or selectors, which are methods that do not modify the state of
objects
■ Public mutators, or modifiers, which are methods that modify the state of objects
■ Nonpublic constructors and auxiliary methods
II mutators
public void insert(Object item, inti) { • • • }
public void insertHead(Object item) { }
public void insertLast(Object item) { . . . }
public Object remove(int i) { . . . }
public Object removeHead() { . .. }
public Object removeLast O { . .. }
II fields
protected Node head, tail;
protected int count;
// auxiliary nested class
static protected class Node {
Object element;
Node next, prev;
}
}
For a field named attr, an accessor, also known as a getter, named getAttr()
should be provided to access the value of the field. Optionally, a mutator of the field,
also known as a setter, named setAttr() may be provided to modify the value of the
field. When a field is of type boolean, its accessor should be named isAttr( ).
6.1 Design and Implementation of Classes • 211
Figure 6.3
Rectangular and
polar coordinates.
y
According to this guideline, in the Point class, instead of making x and y public,
they should be nonpublic, and accessors and mutators should be provided for them.
One may think that public fields such as x and yin Point are straightforward and
hannless and that accessors and mutators for such simple field are overkill. However,
that assumption is not correct. Let us consider the clas PolarPoint. It extends the
Point class and represents a point in both rectangular coordinate (x, y) and polar
coordinates (r, a), where r is the radius (the distance from the point to the origin) and
a is the angle between the x ax.is and the line that connects the point and the origin,
as depicted in Figure 6.3.
x = r * cos(a) /\ y = r * sin(a)
If the fields r and a were public, a client would be able to do the following and leave
the object in an inconsistent state:
Using mutators setR () and setA () , we can maintain the invariant by adjusting
fields x and y accordingly when field r or a is modified. Making fields r and a
nonpublic and requiring clients to use setR () and set A() to modify these fields
ensures that the instances of PolarPoint remain in consistent states.
PolarPoi nt p • new PolarPoint();
p . setR(l00 . 0); // p remains consistent
6.1 Design and Implementation of Classes • 21 3
If the fields x and y were public in Point, the Point class itself would not be
harmed because x and y are unconstrained. However, that would allow the clients of
PolarPoint to modify directly the fields x and y, resulting in an inconsistent state.
Thus seemingly harmless public fields can hinder the ability of subclasses to maintain
consistency. However, when the x and y fields are nonpublic, their modifiers, setX ()
and setY (), can be overridden in PolarPoint to ensure consistency.
If a class has no public fields, the clients of the class can only access the
functionality or services provided by the class via its public methods. Therefore, a
corollary of the avoid public field design guideline is the following design guide-
line:
The advantages of separating interface from implementation are that the imple-
mentation details are completely hidden from the client , o change in implementa-
tion will not affect clients. The List interface hown in Figure 6.1 define the interface
of the list we want to implement.
Implementation of the li t can be provided in everal different clas e , all of
which implement the List interface. For example, both of the following classes,
214 • From Building Blocks to Projects
Linked.List and DynamicArray, implement the List interface, but their imple-
mentation can be entirely different:
public class LinkedList implements List {
(body of LinkedList)
}
copied verbatim to the documentation page, followed by a list of tags, which will be
formatted by j avadoc in a consistent style. The commonly used tags are summarized
in the following table:
Tag Description
aauthor The author(s) of the feature. Multiple author tags are allowed.
aversion The current version of the feature.
Gsince When or in which version the feature first appeared.
aparam The meaning and the acceptable values of a parameter of a method.
A method may have multiple param tags.
areturn The meaning and the possible values of the return value of a
method.
asee A link to the documentation of other related classes or method .
athrows An unhandled exception that might be thrown by a method
For each public class or interface, a doc comment documenting the class or
interface should be provided. The following is an example of doc comment describing
the LinkedList class:
; ..
• Class LinkedList implements the interface List , which is an
• ordered collection of elements that can be accessed through
• an index . The size of a LinkedList can grow as needed .
Each interface or class defines a set of services, i.e., the public methods, to be
provided by an implementor of the interface or by the class itself. The method
declarations only specify the type of the methods, not the behavior. A contract of
a method is a specification of the behavior of the method, i.e., what service is to be
provided by the method. Contracts of methods are often specified informally or even
left unspecified. Informal or missing contracts are major contributors of problems
in software development. The potential problems of informally specified contracts
include these:
■ Incompleteness and silence on some aspects of the behavior.
■ Ambiguity and multiple interpretations.
■ Contradictions with other contracts.
Informal contracts often lead to misunderstandings and misuses of the services. An
alternative is to specify the contracts of methods formally, i.e., mathematically. The
advantages of formally specified contracts include the following:
■ Precision and unambiguousness.
■ Run-time checking to catch violations of the contract by the implementation as
well as misuses of the services by the clients.
■ Facilitation of reasoning about the behavior of both the implementation and the
clients.
There are specification languages that support formally specifying the complete
behavior of software systems, including the algebraic specification language Larch
and model-based specification languages Z and VDM. However, the effective use
of these formal specification languages requires extensive skills in mathematics. Its
high cost is only justifiable for highly critical applications. Nevertheless, limited use
of formal ly specified contracts can be easy and very cost-effective . We advocate
the use of formal contracts to define the behavior of all methods in all classes. The
formal contracts do not necessarily need to be complete. Their main goal is to ensure
that certain constraints of the services are met and that certain conditions of the
implementations are maintained.
The contract of a method can be specified using preconditions and postcondition
of the method. A precondition of a method is a boolean expression that mu t hold
when the method i invoked. In other words, a method may not be invoked when it
6.2 Contracts and Invariants • 21 7
I**
* Opre precondition
* Opost postcondition
•I
public void aMethod( ) {
II . ..
}
The following two boolean operators are often quite useful and can be used in
preconditions and postconditions.
==> logical implication, i.e., a => b, if and only if a is false or both a and
bare true.
<=> logical equivalence, i.e., a <=> b, if and only if both a and b are true
or both a and bare false .
Let us have a look at the formal contracts of ome of the method of the List
interface:
I**
• Returns the number of elements in t he li st .
•
• Opre true
• Opost Onochange
•I
public int size ();
2. The Opre and Opost tags and other tags for documenting con1ruc1 an~ invari~ts nre nol supported by
the slundard javadoc tool. However, there are third-party tools thnt provide vnnous levels of support for
these tags.
21 8 • From Building Blocks to Projects
/u
* Returns true if and only if the list is empty.
*
• Opre true
• Opost Oresult <=> size() > 0
* Opost Onochange
•I
public boolean isEmpty();
/o
* Returns the i-th element in the list.
*
* Opre i >= 0 kt i < size()
* Opost Onochange
•I
public Object element(int i);
The element () method has a precondition. It means that the method can only
be invoked when the precondition is true. Otherwise, an exception will be thrown.
The postcondition only specifies that the state of the list does not change when this
method is invoked. However, it does not completely specify the behavior of the
method. The simple specification language we use here is inadequate to completely
specify some of the behaviors of the methods. A number of full-blown specification
languages, including Larch, VDM, and Z, are capable of completely specifying the
behaviors of programs and facilitating reasoning about their behaviors. However,
our goal of using formally specified contracts is much more modest. We only try
to formally constrain the behavior as much as possible for the purpose of precisely
documenting the behavior and instrumenting the source code with assertions derived
from the contracts. Such assertions can be extremely effective in assisting the testing
and debugging of programs.
The following are the contracts for methods head () and last (). They use the
element () method to constrain their results.
I**
* Returns the first element in the list .
•
* Opre !isEmpty()
* Opost Oresult •• element(O)
• Opost Onochange
•I
public Object head();
6.2 Contracts and Invariants ■ 219
I**
• Returns the last element in the list .
*
• Opre !isEmpty()
* Opost Oresult == element(size () - 1)
• Opost Onochange
•I
public Object last();
When specifying the postcondition of a mutator, i.e., a method that changes the
state of the object, it is necessary to distinguish the state of the object before and after
the method invocation. By default, the values of the expressions in the postcondition
are evaluated with respect to the after state of the object, i.e., the state of the object
immediately after the method returns. To refer to the values of expressions evaluated in
the before state, i.e., the state of the object immediately before the method is invoked,
we use the following prestate notation.
Expression Opre The value of Expression evaluated in the slate just before the
method is invoked, i.e., the prestate.
where x is a new variable name, Range i a range expre ion. which pecifies a
collection of objects, and fa:pression i a boolean expre ion. The range expres ion
can be one of the following:
[ m .. II] Both m and II are integer expre ion . It define an integer range
from m to 11 , inclusive.
Expression The Etpressio11 mu t evaluate to a Collection. Enumeration: or
Iterator object ( ee Section ,_). It define a r~nge th~t consists
of all the object in the collection. enumera1ton, or Herator,
respectively.
ClassName It define a range con i ting of all the in tance. of the clas .
220 ■ From Building Blocks to Projects
/u
* Inserts a new element into the list at the i-th position.
*
* Opre item!= null kk i >= 0 kk i <= size()
* Opost size()== size()Opre + 1
* Opost Oforall k: [O .. size() - 1] 0
* (k < i ==> element(k)Opre == element(k)) kk
* (k == i ==> itemOpre == element(k)) kk
* (k > i ==> element(k - l)Opre == element(k)
•I
public void insert(Object item, inti);
The precondition of the insert() method specifies the possible range of the
insertion positions, from O to size O. The postconditions of the insert () method
are the following:
The insertHead () and insertLast () methods are special cases of the in-
sert () method, where the insertion position is at the begining and the end of the list,
respectively.
/u
* Inserts a new element at the head of the list.
*
* Opre item != null
* Opost size()== size()Opre + 1
* Opost itemOpre == element(O)
• Opost Oforall k: [1 .. size() - 1] 0 element(k - l)Opre -- element(k)
•I
public void insertHead(Object item);
/u
* Inserts a new element at the end of the list .
•
* Opre item I• null
* Opost size()•• size()Opre + 1
* Opost itemOpre •• element(size() - 1)
* Opost Oforall k: [O . . size() - 2] O element(k)Opre == element(k)
•I
public void insertLast(Object item);
6.2 Contracts and Invariants ■ 221
1. The return value is the element at the ith before the method is invoked, i.e., the
element being removed.
2. The size of the list will decrease by l after the removal.
3. All the elements before the removal position remain unchanged, and all the
elements after the removal position shift one position toward the front.
I**
* Remove the element at the i-th position .
*
* Opre size()> 0
* Opre i >= 0 && i < size ()
* Opost Oresult = element(i)Opre
* Opost size()== size ()Opre - 1
* Opost Oforall k : [O . . size () - 1] 0
* (k < i ==> element (k) Opre == element (k)) &&
* (k >= i => element (k + l ) Opre = element (k))
•I
public Object remove(int i);
The removeHead () and removeLast () methods are special cases of the re-
move () method, where the removal position is at the begining and the end of the list,
respectively.
I**
* Remove the element at the head.
*
* Opre size()> 0
* Opost Oresult = element(O)Opre
* Opost Oforall k : [l . . size () 1] O element (k + l)Opre -- element(k)
•I
public Object removeHead( );
I**
* Remove the element at the end .
*
* Opre size()> 0
* @post Oresult = element(size () - l )Opre
* Opost Oforall k : (0 . . size 0 1] o element (k)Opre -- element (k)
•I
public Object removeLast ();
Specifying the contact of the methods in the List interface requires that all
classes that implement the List interface must honor the e contracts. In other words,
222 ■ From Building Blocks to Projects
; ..
LinkedList class to check whether the invariants hold.
/o
* Oinvariant Expression
•I
public class AClass {
II . . .
}
The expression in the @invariant tag must be a boolean expression. The @in-
variant tag may occur multiple times. The invariant defined by multiple occurrence
of the @invariant tag is the conjunction of all the boolean expressions in these @in-
variant tags. The implication and equivalence operators, as well as the quantified
expressions, can also be used in invariants. Since invariants alway deal with the tate
of objects at a given moment, the prestate notation is not meaningful in invariant .
The invariant for the LinkedList class can be expre ed a follow :
I /o
* Oinvariant
*I
_wellformed ()
The implementer of a clas i respon ible for ensuring that all public methods
pre erve the invariants concerning the well-fom1edness of the implementation. The
requirements are stated in the following de ign guideline:
If these requirements are satisfied, then the invocation of any public method in
any order should always result in a well-fonned state.
6.2.3 Assertions
3. A scrtions arc introduced in JOK 1.4. A JDK 1.4 or later is necessary to use assertions.
6.2 Contracts and Invariants • 225
methods, and the postcondition i Iran lated into an assertion ju t before the return
statement.
I**
* Returns the first element in the list.
*
* Opre !isEmpty( )
* @post @result== element (O)
*I
public Object head () {
assert !isEmpty();
Object result= (head !=null ? head . item nul l ) ;
assert result = element(O) ;
2
return result ;
}
To compile a program with assertions requires JDK 1.4 or higher and the
-source switch must be used, as follows:
You may notice that not all the postconditions are being asserted. Postconditions
that deal with the prestate of objects are usually more complicated to assert. It is often
necessary to clone the object before it is modified. We will show another version
of this method that asserts the postcondition involving the object in prestate (see
p. 233).
Using assertions derived from the preconditions for all methods is known as
defensive programming. Its aim is to prevent a component from being misused. An
assertion failure of a precondition indicates that a client attempted to use the service
improperly. In the case of an error, assertions on preconditions can help to e stablish
whether the error is caused by the implementation of the service or the improper use
of the service.
Using assertions derived from the postconditions and the invariants of classes can
be extremely effective in assisting the unit testing and debugging of the implemen-
tation of classes. Incorrect test results can show the symptoms of errors. It is often
a tedious, time-consuming, and nontrivial process to locate the source of the errors.
An assertion failure of a postcondition or invariant usually identifies the source of an
error.
the implementation of class S must honor the contract of class C. This requirement
can be stated as the follow_ing important design guideline:
. ----
6.3 THE CANONICAL FORM OF CLASSES
The canonical form of public classes ensures that instance of those clas e wi!J
be well behaved when they are manipulated by the Java run-time environment and
many other classes, such as those classes in the collection framework (see Section 8.2
[p. 308]).
The relevant methods involved in the canonical form are discussed in the follow-
ing ub eclions.
The no-arg constructor of a class is useful because it allows instances of the clas
to be created dynamically by the Java virtual machine at run time. As a result, the
objects are not created by u ing the new operator directly or indirectly anywhere in
the program. The program that u e the object may not have any reference to the cla s
or any knowledge of the existence of the class. The class will be made available to the
program at run time, and the objects will be created by the Java virtual machine. Many
important Java applications and frameworks depend on the ability to load classe and
create instances al run time. Therefore, to make your class as widely reu able a
possible, it should provide a no-arg constructor.
The dynamic loading mechanism is one of the more useful feature of Java.
However, a full discussion of the mechanism is beyond the scope of this book.
The equals() method defines the equality of object states on a per-class basis. The
default implementation of the equals() method in the Object class tests the identity
of objects; that is, o1. equals (o2) if and only if both o1 and o2 refer to the same
object. Most classes should override this method and define the notion of equality
based on the contents, i.e., the states, of the objects, not their identities.
The contract of the equals () method is that all implementation must satisfy the
following conditions:
return true;
}
return false;
I
}
if (p != otherObj.p)
return false ;
The values of some fields may be temporary, derived from other fields. or
nonessential. These fields are called insignificant fields. They can be excluded from
the comparison for equality.
The following is an implementation of the equals () method of the LinkedLi st
class. Two lists are considered equal if they are of the same length and the element
at the same position of the two lists are pairwise equal. ote that the list referenced
by parameter other does not need to be a linked list.
The following are a few ways to combine the hash codes of individual fields:
1. Bitwise-or
hash = hash<< n I c
hash= hash* p + c
The clone O method returns a clone of the object it elf. It is analogous to the copy
constructor in C++. The contract of the clone() method i the following:
■ The cloned object must not be the same object as the original; i.e., o . clone()
!= 0.
■ The cloned object and the original object are instance of the same class.
■ The cloned object must be equal to the original object; i.e., o. clone()
. equals (o) .
■ For a class that implements the marker interface Cloneable, it create a shallow
copy of the original object.
■ For a class that does not implement the marker interface Cloneable. it throw
a CloneNotSupported.Exception.
A shallow copy means that the value of each field of the original object i copied to
the corresponding field of the cloned object, whether the field i of primitive rype or
reference type. Shallow copying is adequate for field of primitive type . For field of
reference types, only the references are copied, and the actual object referenced by
the fields are not cloned. A deep copy mean that the objects referenced by the field
of reference type are al o cloned. The difference between hallo, and deep copie
is illustrated in Figure 6.4 for the LinkedList example.
For a class to support cloning, it i nece ary to implement the Cloneable
interface, oven-ide the clone () method, and declare it public. The folio, ing is
another version of the Point clas that upport cloning. ince all the field of the
Point clas are of primitive type . the default hallow cop implementation in the
Object class is sufficient.
II . ..
}
II client code
Point pl= new Point();
Point p2 = (Point) pl . clone();
An important rule in implementing the clone () method is that the cloned object
should not be created using the new operator, unless it is a final class. This rule exists
because of the requirement that the cloned object and the original object are instances
of the same class. If the new operator were used to create the cloned objects, and if
there were a subclass that did override the clone() method, then a clone of an object
Figure 6.4
list1 listl
Copies in cloning: head: head:
(a) shallow copy;
(b) dttp copy. tail: tail:
listl. clone()
head: head: 1
tail: tail:
count: 3
3
(a) {b)
6.3 The Canonical Fonn of Classes • 233
of that subclass would be an instance of its superclass, since the inherited clone()
method was used. The right way to create the cloned objects of a nonfinal class C is
I**
* Inserts a new element into the list at the i-th position.
*
* Opre item != null && i >= 0 && i <= size ()
* Opost size() == size()Opre + 1
* Opost Oforall k : (0 . . size() - 1) 0
* (k < i ••> element(k)Opre •• element(k)) ll
* (k •• i ••> itemOpre •• element(k)) ll
* (k > i ••> element(k - 1)0pre •• element(k)
•I
public void insert(Object item, inti) {
assert item != null && i >= 0 && i <= size () ;
assert _wellformed();
int size_pre = size ( );
LinkedList this_pre • null;
try {
this_pre • (LinkedList) clone();
} catch (CloneNotSupportedException e) {}
if (i <= 0) {
insertHead(item);
} else if (i >= count) {
insertLast(item);
} else {
// i > O && i < count;
Node n = head;
for (int j = O; n != null && j < i - 1; j ++) {
n = n .next;
}
Node node= new Node () ;
node . item = item ;
node.next= n .next ;
node.prev = n;
234 • From Building Blocks to Projects
node.next.prev = node;
n.next = node;
count++;
}
int size_post =size();
assert size_post == size_pre + 1;
boolean insertOK • true;
for (int k • O; insertOK && k < size(); k++) {
if (k < i) {
insertOK • (tbis_pre.element(k) •• element(k)) ;
} else if (k •• i) {
insertOK • (item•• element(k)) ;
} else {
insertOK • (tbis _pre .element(k - 1) •• element(k));
}
}
assert insertOK;
assert _wellformed();
}
We have previously discussed the toString () method (see Section 4.4.8 [p. 122]).
It returns a string representation of an object. The toString () method is very useful
in many situations, especially for testing and debugging programs. The result of the
toString () method should include all the fields of the object.
The following code fragment implements the toStringO method of the
LinkedList class:
public String toString() {
StringBuffer s = new StringBuffer();
inti= O;
for (Node node= head; node != null; node= node.next, i++) {
s.append ("[" + i +"]="+node.element+ "\n");
}
return s.toString();
}
6.3.6 Serialization
customize the way its instances are serialized by implementing the methods read-
Object O and wri teObj ect O. Serialization is discussed further in Section 8.4. l
[p. 367].
6A UNIT TESTING
The test invokes most of the methods of the Linked.List class. However, the test
is rather ad hoc. It only prints out the results without any indication of the correctness
of the test results. It requires a visual inspection of the results by a human tester
to determine the correctness of the results. A complete test of nontrivial classes i
usually much more extensive and often needs to be repeated many times. Depending
on human visual inspection to determine the correctness of the results i extemely
time-consuming, unreliable, and often impractical.
The testing results can be automatically verified in the testing program. The
following is another vision of the program testing the implementation of LinkedLi st
clas . In this version, the expected results after each testing step are included in the
test program, as a two-dimentional integer array results . The actual results and the
expected results are compared, and any discrepencies are reported.
package test ;
import mylist.•;
public class LinkedListTest2 {
protected static i nt [)[) results = {
{ 2, 1, 3, 7, 6, 5, 4 }, II result after insertion
{ 1, 3, 6, 5 }, II result after removal
{ 1, 3, 6, 5 }, II the cloned list
{ 1, 3, 6, 5 }, II the original list after removing the
head from the cloned list
{3,6,5}, II the cloned list after removing t he
head from the cloned list
};
--
6.4 Unit Testing ■ 23 7
1. removeHead () ;
l.removeLast();
l . remove(2);
12 . removeHead();
System.out.println ( "Original list" );
System.out . println (l);
if (!TestUtil . match(l, TestUtil . tointegerArray(results[3)) )) {
System.out.println("Result mismatch" );
testPassed = false ;
}
System.out.println("Cloned list" );
System . out.println(l2) ;
if (!TestUtil .match (l2, TestUtil.tointegerArray(results[4]))) {
System .out . println ( "Result mismatch");
testPassed = false;
}
238 ■ From Building Blocks to Projects
if (testPassed) {
System. out. println ( "Test passed. ") ;
} else {
System . out .println("Test failed .");
}
The TestUtil class provides two auxiliary methods that are used to compare
the lists against the expected results:
This test program compares the lists to their expected results after each testing
step. It also prints out a simple message at the end indicating whether all the test
results are correct.
When dealing with a large and extensive set of test cases, it is usually more convenient
to use a unit-testing tool. JUnit 4 is a flexible, easy-to-use, and open-source unit-testing
tool for Java programs. The following is a template of typical JUnit test programs:
Each test program, that is, test suite, is written as a subclass of the TestCase
class. The constructor with the test suite name is required. Each test case i written as
a separate test method. The names of test methods must begin with the prefix test.
The suite () method in the test program is required. It aggregates all the test cases
in the class into a test suite.
The Linked.List Uni tTest class that follows i a simple test program using
JUnit to test the implementation of the Linked.List clas . It consists of three
test cases as the following three method of the test cla s: testinsert () ,
4. JUni t is available al www . juni t . org. Documentation and other infom1ation about JUnit can be found
at the site.
240 ■ From Building Blocks to Projects
testRemove () , and testClone O. The assert True O method in the test cases
as erts that the boolean expression argument must be true, otherwise an error is
recorded.
package unittest ;
assertTrue(TestUti l. match(l,
TestUti l.tolntegerArray(new int[] {2, 1, 3 , 7, 6, 5 , 4 } )));
}
12.removeHead();
assertTrue ( "Match 3"' TestUtil. match(l1, TestUtil. tolntegerArray(ial) )) ;
assertTrue("Match 4", TestUtil .match(l2 , Testutil . tolntegerArray(ia2))) ;
assertTrue( "Not equal", !11.equals(l2)) ;
}
To compile and run the test program, JUnit must be installed and the libr fil
j unit.jar must be included in the CLASSPATH. The following command co:ile:
the JUnit test class:5
venus¾ j avac -classpath . :]Unit home/ juni t . jar -source 1. 4
unittest/LinkedListUnitTest.java
where ]Unit home is the installation directory of JUnit on your computer. JUnit
provides test runners to execute the test cases. The text-based JUnit test runner can
be invoked as follows:5
venus¾ java -classpath . :JUnithome/junit.jar -ea junit . textui.TestRunner
unittest.Lin.kedListUnitTest
The result of running the GUI-based JU nit test runner is shown in Figure 6.5.
;._
Figure 6.5 a
JUnlt
GUI-based JUnit
test runner. Test dUS name:
t.:lu:.:.n:..it_t,_s_t._L_ln_k_e_d_L_ls_tU_nl_tT
~;e;-s_t_ -=========---'I~ Q I. .___. .
@ RelHd duses every run
...
►
rinished: 0.207 seconds Exit
The term proj ect build refers to both the process and the result of producing an
executable deliverable of the project. In an iterative development proce , delivering a
new project build is a key element in every iteration, starting from the very beginning.
Each build process often involves many repetitive tasks, such as compiling, te ting.
generating documentation, and packaging source or binary file , and so forth . A large-
scale software system usually consists of numerou subsy tern and component .
Each of the subsystems and components couJd be built separately. Thu , the number
of repetitive tasks involved in a build could multiply. It is al o quite common that
many of the tasks are interdependent. We say task T2 depend on task T 1, if ta k T1
mu t be completed before task T2 can be carried out.
A build tool offers assistance in managing the build proces in the following
ways:
1. Allowing repetitive task to be carried out with a little effort a po ible.
2. Automatically detennining and enforcing the dependencie an1ong the ta k
while carrying out the task .
Ant 6 i a powerful, easy-to-use, open- ource build tool for Java project . The proce
of project building is pecified in a b11ildfile in ML yntax. By default. the build file
is named build. xml . The following i a template of typical build file :
6. Ant uvailablc al j a.kart a . apache . org. Documentation and other infonnation about Ant can be found
al the site.
244 • From Building Blocks to Projects
Each build file defines the process for building a single project. The main body of
the build file consists of a set of property definitions and a set of target definitions. A
property is simply a name-value pair. The property name must be unique. A property
definition associates a value to a property name.
<property name•"property-name "value•"property-value" />
The value of a defined property can be referenced in the rest of the build file as
${property-name}.
A target is a step in the build process, which consists of a list of tasks that should
be perfonned together. Each target has a name and may also have a dependency list,
which is a comma-separated list of the names of other targets that this target depends
on. Target ft depends on another target t 2 if target t2 must be successfully completed
before target ft can be executed. A typical task definition is in the following form:
<target name•"target-name" depends•"dependent 1, ••• , dependent,,">
. . . task definitions . . .
</target>
Ant supports an extensive set of built-in tasks for building projects. Each task has
a name and various parameters. Commonly used tasks include the following :
Consult the Ant documentation for the details of using these tasks.
Th_e follo-:ving is an example of Ant build files for performing various tasks related
to the linked list classes and the test suites.
<project name•"LinkedList" default="dist">
<!-- set global properties for this build-->
<property name• "src" value="."/>
<property name .. "classes" value•"classes"/>
6.5 Project Build ■ 245
</project>
To perfom1 a build using Ant, you invoke Ant with the de ired build target name
as the argument a follows:
venus¼ ant target-name
For more detail on using Ant, con ult the Ant documentation.
246 ■ From Building Blocks to Projects
CHAPTER SUMMARY
FURTHER READINGS
EXERCISES
6.1 Design and implement the classes di cussed • size: returns the number of objects currently
in the case study of Chapter 2: Customer and in the queue.
Address . You should follow the principles • first: returns the object at the front of the
discussed in Section 6. 1 and adopt the canonical queue without removing it from the queue.
form discussed in Section 6.3. • last: returns the object at the end of the
6.2 Make the LineSegment, Rectangle, and queue without removing it from the queue.
Circle classes in Exercise 5.1 conform to Formally specify the contract of each method.
the canonical form discussed in Section 6.3. 6.5 Design and implement a class that implement
6.3 Define an interface for a stack, which supports the Stack interface defined in Exerci e 6.3
the following methods: using an array.
• push: adds a new object onto the top of the (a) Formally pecify the invariant of the cla s.
stack. (b) U e as ertions to en ure that the invariant
• pop: removes the object at the top of the and contract hold at run time.
stack and returns the object. (c) Design and implement test ca e for unit
• size: returns the number of objects currently testing u ing JUnit.
in the stack.
• top: returns the object at the top of the stack 6.6 De ign and implement a clas that implement
without removing it from the stack. the Queue interface defined in Exerci e 6.4
using an array.
Formally pecify the contract of each method.
(a) Formally pecify the invariant of the clas..
6.4 Define an interface for a queue, which upport
(b) U e assertion to en ure that the invariant
the following methods:
and contract hold at run time.
• e11q11e11e: adds a new object at the end of the
(c) De ign and implement te t ca e. for unit
queue.
te ting using JUnit.
• deq11e11e : removes the object at the front of
the queue and returns the object.
Design by Abstraction
CHAPTER OVERVIEW
In this chapter, we introduce design patterns. The main focus of this chapter is designing
reusable and extensible components, using abstract classes, interfaces, and design
patterns. We d iscuss several important design patterns for abstraction: the Template
Method, Strategy, Factory, and Iterator. We also present a case study on the animation of
sorting algorithms.
The concept of p<11tems wa · originally arti ulated b Chri. topht:r Alexander and his
colleague to describe architectural de ·ign . In ·earching for the e sence of great
buildings, great town., und beautiful place , Alexander propo ed in The Timeless Way
249
250 ■ O~ign by Abstraction
Each pattern describes a problem which occurs over and over again in our environ-
ment, and then describes the core of the solution to that problem, in such a way that
you can u e this solution a million time over, without ever doing it the same way
twice. [Alexander et al. 1977]
■ Both are creative processes that unfold within a large design space, which com-
prises all possible designs.
■ The resulting design must satisfy the customer's needs.
■ The resulting design must be feasible to engineer.
■ The designers must balance many competing constraints and requirements.
■ The designers must seek certain intrinsic yet unquantifiable qualities, such as
elegance and extensibility.
The Design Patterns catalog also popularized a common style for describing
design patterns. The description of each design pattern consists of some or all of the
following sections:
The singleton class discussed in Chapter4 [p. 114] is one of the creational pattern
in the Design Patterns catalog. We use the Singleton pattern to illustrate the format
for describing design patterns.
Singleton
'\
static getlnstance() O······· return thelnstance
operation()
getData()
■ Singleton
declares the unique instance of the class as a static variable, and
defines a static method get Instance O for clients to access the unique
instance.
The following program segment is an implementation of the Singleton pattern in
Java.
public class Singleton {
public static Singleton getlnstance () {
return thelnstance;
}
private Singleton() {
(initialize instance fields)
}
7.2.1 Refactoring
• Identifying code segments in a program that implement the same logic, often in
the same exact code, in many different places.
• Capturing this logic in a generic component that is defined once.
• Restructuring the program so that every occurrence of the code segment is
replaced with a reference to the generic component.
Refactoring thus eliminates duplicate code segments, which are hazardous for main-
tenance. It ensures that a bug fix or logic enhancement need be implemented only
once-in the generic component. Without refactoring, every change to a recurring
code segment must be repeated everywhere the code segment occurs, some instances
of which may be overlooked or forgotten. Recurring code segments can easily " drift
apart," making them harder and harder to maintain.
Refactoring must be applied only to code that actually duplicates underlying
logic, however. Not all code that looks alike is alike.
The simplest form of refactoring can be done through the use of f unction or
method invocation. For example, in the following class, the highlighted code segment
occurs in two different contexts:
class Computation {
void method! ( . . . ) {
II . ..
computeStep10 i
computeStep2 0 i
computeStep3 0 i
II . ..
}
void method2 ( . . . ) {
II . ..
computeStepl O i
computeStep2 0 i
computeStep3 0 i
II . ..
}
II . ..
}
254 ■ Design by Abs1raction
The common code sequence can be refactored by introducing a new method com-
puteAll () that contains the recurring code segment. The class can be restructured
as follows:
class RefactoredComputation {
void computeAll () {
computeStep10 ;
computeStep20;
computeStep3 0 ;
}
void metbod1 ( . . . ) {
II . ..
computeAll O ;
II . . .
}
void metbod2(. ) {
II . . .
computeAll O ;
II . ..
}
II . . .
}
The refactored version is functionally identical to the original version. If the code
in the common code segment needs to be modified, it now has to be modified in only
one place (in computeAll ()) instead of several places, as in the original version.
Refactoring by method invocation is effective only when each occurrence of the
recurring code segment is contained within a single method and all the methods that
contain the recurring code segment belong to the same class. When the recurring
code segment involves several methods, such as the methods start (), stop () , and
run() in animation applets-or when the recurring code segment occurs in several
classes-refactoring can be accomplished by using inheritance or delegation.
Refactoring by Inheritance
The refactoring of recurring code segments in different classes can be done through
inheritance. Consider the following example, in which an identical computation
sequence occurs in two different classes:
Each occurrence of the code sequence in the original method can now be
replaced by an invocation of the method compute All () .
class ComputationA class ComputationB
extends Common { extends Common {
void methodl ( . . . ) { void method2 (. . . ) {
II . . . II . ..
computeAll () ; compute All () ;
II . . . II . ..
} }
II . .. II .. .
} }
When extracting common code segments to a superclass, all the field involved
in the computation must also be extracted and moved to the superclas .
Refactoring by Delegation
Refactoring of recurring code segments in different clas e can al o be done through
delegation. To refactor the recurring code equence in the preceding example u ing
delegation, we can introduce a helper class and place the refactored code equence in
the computeAll () method of the helper cla
class Helper {
void computeAll( . . ) {
computeStep10 ;
computeStep2 0 ;
computeStep3 0 ;
}
}
PURPOSE
This example demonstrates refactoring by inheritance.
DESCRIPTION
A generic animation applet class AnimationApplet is defined by extracting the
common elements in animation applets: the methods start() , stop() , and run ().
The digital clock applet presented in Example 4.10 [p. 149] is reimplemented, using
the AnimationApplet class.
SOLUTION
The relationship among the classes Applet , AnimationApplet (the generic ani-
mation applet class), and Digi ta1Clock3 (the reimplemented digital clock applet
using the generic animation applet) are shown in Figure 7 .1. The methods of Anima-
t ionApplet are summarized in the following table:
Method Description
. Th_e setDelay O and getDelay O methods are introduced to make the generic
anunat1on applet class more extensible, allowing each concrete animation to set its
own refresh rate. Extensibility is essential to all generic components.
7.2 Designing Generic Components ■ 257
Digita1Clock3
paint()
.,
- .. .. -
-- -
258 ■ Design by Abstraction
Note that the fields used by the methods start () , stop () , and run () are also
tracted to the superclass. The AnimationApplet class avoids duplication of the
::art() , stop() , and run() methods in every animation applet. A pecific, or
concrete, animation applet can simply extend AnimationApplet and override the
paint() method to draw animation frames.
PURPOSE
This example illustrates how to resolve the complications involved in refactoring by
inheritance. It also addresses issues of extensibility and adaptability in the generic
animation applet.
DESCRIPTION
A double-buffered generic animation applet class DBAnimationApplet is defined,
using refactoring by inheritance. The bouncing ball applet in Example 5.4 [p. 195] is
reimplemented, using the DBAnimati onApplet class.
KEY ISSUES
■ Accommodating various sizes of the view area.
■ Allowing the subclasses to decide whether to use double-buffering.
■ Refactoring common code segments that contain changeable parts.
SOLUTION
With regard to the first key issue, in order to initialize the off-screen image buffer, we
need to know the dimensions of the viewing area. The problem is that the dimensions
of the viewing area are not known until the ini t () method is invoked. A simplistic
solution to this problem is to override the ini t () method in the generic animation
applet.
public class DBAnimationApplet extends AnimationApplet {
protected Dimension dim;
protected Image im;
protected Graphics offscreen;
public void i nit() {
dim = getSize () ;
im = createimage(dim . width, dim .height);
offscreen = im.getGraphics( );
}
II ...
}
This approach properly initializes the off-screen image buffer. However, a prob-
lem arises if a subclass of DBAnimationApplet require additional initialization to
be done in the ini t () method, such as getting parameters. Then it is necessary for
the ini t O method in the subclass to call super. init () to initialize the off-screen
image buffer properly.
public class MyAnimation extends DBAnimationApplet {
public void init() {
// additional Initialization specific to MyAnimatlon
String param = getParameter ( . . . ) ;
II . . .
-
260 • Design by Abstraction
Not invoking super. ini t () will cause a null-pointer exception. The burden
of following this convention is entirely on the implementer of subclasses because its
violation will not be detected by the Java compiler. For various reasons, violation
of such conventions are very common, and they often lead to faults, or bugs, that
are difficult to trace. Although using a component without following the proper
conventions is a misuse of the component, designers of reusable components must
do their best to prevent the components being misused.
A better solution for initializing the off-screen image buffer is to introduce a new
method initAnimator O for subclasses to perform subclass-specific initializations.
The ini t () method is made final, so that it cannot be accidentally overridden by the
subclasses. This approach ensures that initializations of dim, im, and off screen will
always be done properly. A null implementation is provided for ini tAnimator () .
A subclass needs to override this method only when there are subclas - pecific
initializations.
The second key issue involves giving the subclas es the flexibility to decide
whether to use double-buffering. Hence the generic animation applet should accom-
modate two scenarios:
The generic animation class should unify the two scenario o that it can handle
both. A boolean variable can be used to control the behavior of the update () method.
public class DBAnimationApplet extends AnimationApplet {
protected boolean doubleBuffered ;
void update(Graphics g) {
if (doubleBuffered) {
II do double-buffering
} else {
super . update (g) ; // use the default implementation
}
}
II . . .
}
The third key issue involves the use of double-buffering. The part of the up-
date () method that deal with double-buffering i common to all animation applet ,
whereas the part that deals with painting the frame varie from applet to applet. The
generic animation clas should refactor the common part and allow its ubclas e to
provide the variable part.
The refactoring here i a bit more complicated than before, ince the common
code egments are intemlixed with context-·pecific code. Let u con ider a more
general situation. Two method , methodl () and method2 () , have ome common
code segments that are intennixed with ome context- pecific code:
How can the common code segments be refactored? Two approaches are avail-
able. The first approach is to refactor each common code segment into a separate
method by using the refactoring by inheritance technique (p. 254].
class Common {
void commonCodel () {
(common code segment I}
}
void commonCode2 () {
(common code segment 2}
}
}
This approach works, and it is a perfectly good solution when the two common
code segments are relatively independent. However, this approach is error-prone when
the two common code segments are closely related and are merely pieces of a larger
process. Breaking the common code segments into two separate methods breaks the
logical flow and hampers the readability of the code. The invocation of common-
Code1 () and commonCode2 () in each context must be carefully coordinated . If one
of them is omitted or they are invoked in a different order, unpredictable outcomes
may result.
The second, more generic, approach is to extract the entire method that contai ns
both common and context-specific code to a superclass and then refactor the context-
specific code by introducing a new method as a placeholder, which is intended to be
overridden and customized in each subclass.
class Common {
void method( . . . ) {
(common code segment I)
contextSpecificCode ();
(common code segment 2)
}
void contextSpecificCode() { .. . }
}
protected DBAnimationApplet() {
this.doubleBuffered = true;
}
Figure 7.2
I Applet I'-' AnimationApplet /1
"
DBAnimationApplet
start() update()
A double-buffered
stop() paint()
generic animation
run() init()
applet.
setDelay() initAnimator()
getDelay() paintFrame()
I
Digita1Clock3
i
BouncingBa112
paint()
initAnlmator()
palntFrame()
7.2 Designing Generic Components • 265
The ini tAnimatorO and paintFrame () methods are protected, as they are
intended only for the subclasses to override. The update() , paint() , and ini t ()
methods are final so that the subclasses cannot alter their implementation. The con-
ventions for using the double-buffered generic animation applet class are as follows:
The bouncing ball applet developed in Chapter 5 can be reimplemented by using the
DBAnimationApplet class.
Protected int x, y;
dy = - 4;
protected int dx = - 2 '
protected int radius = 20; .
protecte d C0 lor color = Color.green,
■
}
. .
The precedin~ example illustra_tes t~e use of an abstract class that_serves as a template
for classes with shared funct1onahty. An abstract class contams behavior that is
common to all of its subclas es. The common behavior is encapsulated in nonabstract
methods, which may even be declared final to prevent any modification. Using an
ab tract class en ures that all subclasses will inherit the same common behavior (i.e.,
implementation). The abstract methods in such templates require that context-specific
behavior be implemented for each concrete subclass.
The abstract method paint Frame () acts as a placeholder for the behavior that
is implemented differently for each specific context. We call such methods hook
methods, upon which context-specific behavior may be hung, or implemented. The
paintFrame () hook is placed, i.e., invoked, within the method update() , which
is common to all concrete animation applets. Methods containing hooks are called
te~plate._methods.
The double-buffered generic animation applet we just discussed illustrates the
Template Method design pattern. The abstract method paintFrame () represents
the behavior that is changeable, and its implementation is deferred to the concrete
animation applets. The paintFrame () method is a hook method. Using the hook
method, we are able to define the update() method, which represents a behavior
common to all the concrete animation applets. The update () method is a template
method. A template method uses hook methods to define a common behavior. Tem-
plate methods describe the fixed behaviors of a generic class, which are sometime
called frozen spots. Hook methods indicate the changeable behaviors of a generic
class, which are sometimes called hot spots.
GenericC/ass
templateMethod() O '\
hookMethod 1() hookMethod1 ()
hookMethod2()
0, hookMethod2()
ConcreteClass
hookMethod1 ()
hookMethod2()
■ GenericClass (e.g. , DBAni rnat i onApplet), which defines ab tract hook meth-
ods (e.g., paintFrarne O) that concrete subclasses (e.g., BouncingBall2)
override to implement steps of an algorithm .and implements a template method
(e.g., update O ) that defines the skeleton of an algorithm by calling the hook
me thods.
■ ConcreteClass (e.g., Bounc i ngBall2), which implement the hook methods
(e.g., paintFrarne O) to carry out subclass-specific steps of the algorithm de-
fined in the template method .
In the Template Method design pattern, hook method do not have to be abstract.
T he generic c las may provide default implementations for the hook methods. Thus,
the subclasses have the option of overriding the hook method or u ing the default
imple mentation. The initAnirnator O method in DBAnimation.Applet is a non-
abstract hook method with a default implementation. The i n i t () method is another
te mplate method.
The following example illustrates the u e of ab tract classe and the Template
Method desig n pattern:
PURPOSE
To illu trate the u e of the Template Method de ign pattern.
DESCRIPTION
De ign and implement a generic func1i.0 11 pl~tter applet Plotter for _plo tting arbitrary
sing le-variable func tion on a two-d11ne11s1on~l pace. T he genenc plo tter_~hould
capture the com mon behavior relaled to drawing and leave onJy the defi rntto n of
-- 268 • Design by Abstraction
y = sin x
A screen shot of running the Plot Sine applet is shown in Figure 7 .3.
KEY ISSUE
To represent an arbitrary single-variable function.
SOLUTION
The Template Method pattern offers a reasonable solution. The single-variable func-
tion to be plotted can be represented as a hook method in the generic plotter class. The
function-plotting method can then be defined in the generic plotter class as a template
method. The generic plotter class is outlined in the following program segment:
Figure 7.3
Applet Viewer: PlotSine.class lllil a
A screen shot of Applet
PlotSine.
Applet started.
.
-
7.2 Designing Generic Components • 269
PlotSine
tune()
' I
PlotCosine
tune()
The concrete plotter will only need to extend the generic plotter and define the
function to be plotted by overriding the hook method. The program structure is shown
in Figure 7.4.
The generic function plotter Plotter is implemented as an applet. It has the
following methods:
Method Description
The Pl otter applet takes the following parameter set in the applet tag:
Concreteflllldloa
public class PlotSine extends Plotter {
public double func(double x) {
return Math . sin(x);
}
}
7.2.3 Generalizing
PURPOSE
This example demonstrates the generalizing technique and illustrates the use of the
Strategy design pattern.
DESCRIPTION
In this example, the generic function plotter class Plotter is generalized to plot
multiple single-variable functions overlaid on the same two-dimensional space. The
new generic multiple function plotter is named MultiPlotter. A concrete subclass
PlotSineCosine is implemented to plot
KEY ISSUE
How to separate the functions to be plotted from the plotter.
Rgure 7.5
A screen shot of
PlotSineCosine.
Applet started,
272 ■ Design by Abstraction
SOLUTION
One way the oeneric Plotter class can be generalized to plot multiple function s is
by adding mo;e hook methods, one for each function to be plotted.
This approach is rather inflexible and inelegant, allowing only a fixed number of
functions. Segments of nearly identical code will be duplicated to plot each function.
The problem of this approach is that the functions to be plotted and the mechanism to
plot the functions are coupled into the same class. Although the coupling is hannless
for a single-function plotter, it hampers the extensibility of the plotter. A more elegant
olution is to decouple the functions to be plotted from the mechanism to plot them.
Instead of representing each function as a method, we can represent each function
as an objecr.2 Different functions are instances of different fun ction classes. To
allow unifonn manipulation of the functions, all the function classes implement the
following common interface:
lntaface Function
interface Function {
doubl e apply (double x) ;
}
The tructure of the generic multiple function plotter is shown in Figure 7 .6. The
Mul tiPlotter class extends Plotter. It uses a pair of parallel arrays to store the
i
MultiPlotter k>
. Function
initMultiP/otter() apply()
init()
addFunction()
plotFunction()
1
tune();
i
PlotSineCosine
Creates
-- ----- -3>
Sine
apply() Cosine
in itM ulti Plotter() ---- - ----------- --> apply()
Creates
func tions to be plotted and the colors used to plot the functio ns. Although there is a
limit to the number of func tions, MAX_FUNCTIONS, this limit can be easily removed
without affecting any other parts of the program by using an unbounded container,
such as Li s t or Set (see Section 8.2 [p. 308]). The methods of Mu1 tiPl ott e r are
summarized in the following table:
Method Description
The following concrete subclass of Mul tiPlotter class plots two functions :
I
7.2.4 Design Pattern: Strategy
The preceding generic plotter of multiple functions illustrates the Strategy design
pattern. The structure of the Strategy design pattern is shown in the following diagram:
St rategy
Context
A
V Strategy
ContextMethod() algorithm()
I;,.
I
, comm
--
din C and C++
I
Figure 7.7
ICustomerf<fil<=-----------'>:.i/ Service provider A I
(a) Direct coupling
versus (b) abstract ICustomer1-<I<'-------->~/Service provider B I
coupling.
/ Customer/t--=<=----------'>~/ Service provider c I
(a)
Service provider A
Standard
Customer - - - - - ~ service
protocol
Service provider C
(b)
Enumerating the elements in a collection object, such as a list, is one of the most
common operations on any collection. Although it may seem to be a rather trivial
problem, it actually is not. Let us use the simple linked list class LinkedList
di cussed in Section 6.1.1 [p. 207] as an example. Suppose that we have populated
a list with instances of the Course class shown in the following code fragment.
The instance of the Course class represent courses offered at a university. The
isPrerequisi teOf () method returns true when the receiving object represents a
course that is a prerequisite of the course represented by the argument other.
Class COUl"II
public class Course {
public Course(String dept, String code, String title, int level) {
this .dept= dept;
this.code= code;
this .title= title;
this .level= level;
}
What we want to do is simply iterate through the list and print out the courses in
the list.
People who are familiar with procedural programming could easily come up with the
following solution:
LinkedList list;
II populate the courses 11st
for (Li nkedList.Node cur= list.head;
cur I= null;
cur= cur .next) {
System.out .println(cur .element);
}
7.3 Abstract Coupling • 279
This simple solution works only if the fields of LinkedList and the inner class
Node are accessible to the clients of LinkedList, and such accessibility means
that the fields and the inner class must be public. As we discussed earlier, exposing
implementation details by making the fields public not only violates the principle of
encapsulation but could also compromi e the integrity of the class. This olution is a
poor design.
Method Description
Class IterLiat
public class IterList extends LinkedList {
This solution is clean and simple and is adequate for many situations. However,
what if we want to print a table of course prerequisites that lists all the pairs of courses
c 1and c2, in which c1is a prerequisite of c2. To print out the prerequisite table requires
nested loops. A logical solution seems to be the following:
for (list.reset() ; list.hasNext() ; ) {
Course cl= (Course) list .next();
for (list .reset (); list .hasNext() ;) {
Course c2 = (Course) list.next();
if (c1.isPrerequisiteOf(c2)) {
System.out.println(cl + 11 is a prerequisite of 11
+ c2);
}
}
}
Unfortunately, this solution does not work. The outer loop terminates after the
first iteration. The reason is that both the outer and inner loops iterate through the
same list, and when the inner loop completes one pass through the list, the cur index
of the list has been moved to the end of the list, causing the outer loop to terminate.
This type of iteration works only when two or more nested iterations are not on the
same list.3
Now, the following code segment prints the prerequisite table properly:
LinkedListiterator iter1 = new LinkedListLiterator(list);
LinkedListiterator iter2 = new LinkedListLiterator(list);
while (iter1 . hasNext()) {
Course c1 = (Course) iter1.next();
while (iter2 . hasNext ()) {
Course c2 = (Course) iter2.next();
if (c1.isPrerequisite □f(c2)) {
System . out.println(c1 + 11 is a prerequisite of 11 + c2);
}
}
}
Solution D: A Generalization
If all we needed to deal with were LinkedList, Solution C would be perfect. How-
ever, iterating through a collection is a rather common problem, and the collection
could be a list, a hash table, or a set. The implementation in Solution C is tied to a spe-
cific class-LinkedList-whereas the principle is applicable to iterating through
all kinds of collections. We can develop a uniform way to iterate through all kinds of
collections by usin - ~ piing. In other words, clients will not deal directly
with iterators tied to specific implementations; instead, clients will deal with abstract
iterators. The Java Class Library provides two interfaces for abstract iterator .4
interface Iterator {
boolean hasNext();
Object next O;
void remove();
}
interface Enumeration {
boolean hasMoreElements ();
Object nextElement ();
}
4. The reason for having two abstract itera tor interfaces in Java 2 i _to ensure bac~ward com~atibiliry. The
· ·inter face w,..,
En umerat1on ~" pro,•t'dcd by Java l ·O· The Iterator . interface
. was. introduced
. in Java 2 and
·
subsume l he Enumera t 10n 1 ·ntcrfa"e
~ •
Th•'
~
Enumeration interface ts used mainly in Java 1.0.x classes,
such as Ve ct or and Hashtable.
282 • Design by Abstraction
The contracts for the methods of Iterator and Enumeration are as follows:
Method Description
Each collection class that supports iteration should provide a concrete iterator
that implements the Iterator interface. Clients will deal only with the abstract
iterator and need no knowledge of the concrete iterators. They can iterate through
any collection in a uniform way by using the abstract iterator.
To illustrate the implementation of an iterator, we first add the following method
to the List interface:
The iterator () method returns an iterator to iterate through a li st. In the imple-
mentation of the LinkedList class, we add the implementation of the iterator ()
method, which returns a concrete iterator- an instance of the private inner cla s
LinkedListiterator.
Note that the concrete iterator class is private. CLient of the LinkedList cla
need to know nothing about the concrete iterator.
The use of ab tract iterator to iterate unifomlly through different concrete collections
is expres ed as the following de ign pattern:
AbstractCol/ection Iterator
iterator() hasNext()
6. next()
ConcreteCollection ~
r
Concretelterator
..... . . .. .......... . .. ... · ) hasNext()
iteratorO 0 Creates
next()
Figure 7.8
~ M·@l:1 .lclxl
A screen shot of
sort animation ap-
plet I
plet Sort. f
Applet started.
Method Description
Method Description
Method Description
The scramble O method first determines the size of the array to be sorted.
Then it initializes the array and scrambles the numbers in the array. The array ize is
determined by the height of the viewing area. Because each number in the array will be
represented by a horizontal line that is 1 pixel wide-and adjacent line are eparated
by a I-pixel-wide gap-the size of the array i half the height of the viewing area in
pixels. Suppose that n is the size of the array and that the array contain numbers from
0 ton - I.
The paintFrame () method paints a frame based on the current contents of the
array arr. Each number in the array is represented by a horizontal line that is 1 pixel
wide. The length of each line is proportional to the number it represents, with the
longest line proportioned to occupy the entire width of the viewing area.
pause();
++lo;
--hi;
}
}
if (loO < hi) {
quickSort(a, loo, hi);
}
if(lo < hiO) {
quickSort(a, lo, hiO);
}
}
}
Finally, the auxiliary method swap () -wap two number in the array.
The initial version of the sorting algorithm animation applet works, except that the
design of the program is not object oriented. It lumps everything into a single class,
which for those who are accustomed to procedural programming is a natural thing
to do. However, this approach usually results in huge classes that contain a wide
variety of functionalities, and thus lack of cohesion. The resulting programs are overly
complex, inflexible, and difficult to maintain in the long run.
Large classes usually contain loosely coupled elements. Such elements in a class
usually address different concerns in the problem to be solved. Separating these
concerns into different classes can reduce the complexity of the design and enhance
its extensibility and maintainability.
The implementation of the SortAlgori thm class shows some interesting and
common variations of the Strategy design pattern. The first variation is that the
abstract strategy, SortAlgorithm, is an abstract class instead of an interface. Using
an abstract class for the abstract strategy allows some utility methods needed by
concrete strategy classes to be implemented in the abstract strategy class. In the
SortAlgori th.m class, two utility methods, pause () and swap (), are implemented.
The second variation is that the abstract strategy class SortAlgorithm contains a
reference to the client, the Algori th.mAnimator class. This reference allows the
abstract and concrete strategy classes to access the client's interface. In this example,
the reference to Algori th.mAnimator is used in the pause () method to delegate
the invocation of pause () .
Although this solution works, it leaves the client (the Sort2 class) of the Strategy
design pattern (the SortingAlgorithm class) to deal directly with the concrete
strategies (the BubbleSortAlgori thm and QuickSortAlgorithm classes). This
result negates one of the key advantages of using the Strategy design pattern-that
is, to make concrete strategies interchangeable by decoupling the clients from the
strategies. The client should be completely unaware of the concrete strategies.
A better alternative is to use a separate class whose sole respon ibility is to create
instances of concrete sorting algorithms. We call thi class a factory. U ing the factory,
the client (the Sort2 class) can be completely decoupled from the concrete sorting
algorithms.
The factory class can be implemented in everal different ways. To allow the
flexibility of using different factorie and to in ulate the client from future changes in
them, the Strategy design pattern once again can be applied to make different factories
interchangeable. The revised design of the orting algorithm animation is shown in
Figure 7.10. The abstract algorithm factory, AlgorithmFactory, is defined as an
interface. The contract of the hook method makeSortAlgori thm () is
Given tho name of a sorting algorithm, return an instance of a subclass of Sort
Algorithm that implement the named orting algorithm.
\ I
294 • Design by Abstraction
r
A revised design
of sort animation,
using factories.
algorithm() animator
V
sort()
0
algorithm Factory
I
1
I
BubbleSortAlgorithm QuickSortAlgorithm
sort() sort()
A
I
A
I
: Creates : Creates
I I
I
This static algorithm factory is tightly coupled with concrete sorting algorithms.
Adding or changing concrete sorting algorithms requires changes to this factory.
However, using Java's dynamic class-loading capability, we can build a dynamic
sorting algorithm factory that is completely decoupled from the concrete sorting
algorithms; that is, adding or changing concrete sorting algorithms does not affect
the dynamic factory !5
&llimatlon ~ Sort2
public class Sort2 extends AlgorithmAnimator {
protected SortAlgorith.m theAlgorith.m;
protected SortAlgorith.mFactory algorithmFactory;
protected void initAnimator() {
algName = "BubbleSort";
String at = getParameter ( "alg") ;
if (at != null) {
algName = at;
}
algorithmFactory • new StaticAlgoFactory(this);
theAlgorith.m = algorith.mFactory.makeSortAlgorith.m(algName) ;
setDelay(20);
scramble O ;
}
5. An .unplementnuon
.
o1· a dyn,·1mic hctory
' is included in the 011/i11e S11pple111e111 to this book.
_,If
The use of factories to create concrete sorting algorithms illustrates another useful
design pattern- Factory.
The structure of the Factory design pattern is shown in the following diagram:
AbstractFactory
Product
makeProduct()
'
ConcreteFactory
makeProduct()
Creates
-------- - ConcreteProduct
If we examine the revised sorting algorithm animation applet Sort2 closely, we find
there is another loosely coupled component- the display strategy (i.e., the visual rep-
resentation) of the array. The display strategy should be independent of the algorithms
being animated and the animation mechanism. Changing the display strategy should
I
7.4 Design Case Study-Animation of Sorting Algorithms ■ 297
not affect the algorithms and the animation mechanism. The display strategy can be
decoupled from the animation mechanism and the algorithms by using the Strategy
and Factory design patterns so that different display strategies become interchange-
able. The revised design is shown in Figure 7.11.
The interface defines a single method for displaying the array. The parameters
of the method are necessary for displaying the array. This solution is rather straight-
forward, and the interface is small, resulting in low coupling. The change needed
in the main class Sort3 is for the paintFrame O method to delegate the painting
responsibility to the display strategy.
public class Sort3 extends Sort2 {
protected SortDisplay theDisplay;
public void paintFrame(Graphics g) {
theDisplay.display(arr, g, getSize () ) ;
}
II . ..
}
This solution works, but it is inflexible because there is a subtle coupling between
the display strategy and the display size. In the preceding implementation, the ize of
the array is determined in the scramble() method of the Sort class based on the
height of the viewing area. As a result, the scramble () method is tightly coupled to
the displaying scheme.
BarSortDlsplay - - .J
I
SortDisplayFactory I
I
I
......----~-:--~:--:=:-:--7
StaticSortDisplayFactory ______ g!~~~~-------------------J
•
298 • Design by Abstract/on
The second design option is to move the scramble O method to the SortDis-
play interface so that tightly coupled functionalities are in the same class.
public interface SortDisplay {
public void scramble(int a[], Dimension d);
public void display(int a[], Graphics g, Dimension d);
}
The new scramble O method of Sort3 will now also delegate the responsibility
of initializing the array to the display strategy.
public class Sort3 extends Sort2 {
protected SortDisplay theDisplay;
public void scramble() {
theDisplay . scramble(arr, getSize());
}
Now, coupling between the display strategy and its client is reduced. However,
SortDisplay is not very cohesive, as initializing the array and scrambling it have
nothing to do with displaying it. These are basically independent functionalities,
except that they both must agree on the size of the array. So, the third and best
design option is to move scramble O's functionality back into the Sort3 class.
We introduce a new method, getArraySize O , in the SortDisplay interface to
capture the interdependency among these methods.
import java.avt.*;
public interface SortDisplay {
public int getArraySize(Dimension d);
public void display(int a[], Graphics g, Dimension d);
}
The design of the SortDisplay interface illustrates the following design guide-
line:
. ISortJ>i~la
import java.awt . •;
public class HSortDisplay implements SortDi splay {
public int getArraySize (Dimension d) {
return d .height / 2;
}
The revised design allows us to introduce new display strategies easily. 1\vo
variations of the original display strategy are implemented:
■ VSortDisplay: Use vertical lines, with all lines flush against the top edge and
extending downward, as shown in Figure 7.12(a).
■ BSortDisplay: Use vertical lines, with all lines flush against the bottom edge
and extending upward, as shown in Figure 7. l 2(b).
The implementations follow :
Concrete
impor t java . awt.•;
public class VSortDi splay implements SortDispl ay {
public i nt getArr aySi ze (Dimension d) {
retur n ct .wi dt h / 2;
}
Figure 7.12
Screen shots of
Sort3 showing
various display
strategies.
1'1 --------.--l'
. Applet started. Applet started.
(a) (b)
1MfflQfMWM s I□Ix 1
Apple_t _ _ _ _ _ _ _--:
j Applet started.
(c)
: BSortDisP,la
import java.awt.•;
public class BSortDisplay implements SortDisplay {
public int getArraySize(Dimension d) {
return d.width / 2;
}
to the numbers they represent, and different colors are used to represent different
numbers.
Display Factory
The instances of concrete display strategies are also created by a factory. The structure
is similar to the algorithm factories. Implementation of a simple static factory is as
follows:
protected v oi d paintFrame(Graphics g) {
theDisplay.display(arr, g, getSize());
}
}
CHAPTER SUMMARY
of problems that are similar to the original problem. Generalization is often ac-
complished by using abstract classes or interfaces and design patterns, such as
Template Method and Strategy.
• An abstract method is a method whose implementation is deferred to its sub-
classes. Abstract methods are also known as deferred methods. An abstract class
is a class that includes or inherits at least one abstract method.
■ The Template Method design pattern defines the skeleton of an algorithm in a
method, deferring some steps to subclasses and thus allowing the subclasses
to redefine certain steps of the algorithm. The Template Method design pattern
should be used to implement the invariant parts of an algorithm or to refactor and
localize common behavior among subclasses.
■ The Strategy design pattern defines a family of algorithms, encapsulates each
one, and makes them interchangeable. The Strategy design pattern should be
used when many related classes differ only in their behavior or when different
variants of an algorithm are needed.
■ The Factory design pattern defines an interface for creating objects but lets
subclasses decide which class to instantiate and how. The Factory design pattern
should be used when a system should be independent of how its products are
created.
• Important design guidelines include the following:
Re/actor recurring code segments: Recurring code segments based on the
same logic are hazardous to maintenance. They should be refactored so
that the code segment occurs only once. Other occurrences of the code
segment should be replaced with references to the common code.
Maximize extensibility: Rarely can components be reused without adapta-
tion. Extensibility allows components to be extended and adapted to
different contexts. The more extensible a component i , the better the
chance that it may be reused.
Prevent misuses by clients: Well-designed cla se hould prevent po sible
misuses by making violations of the convention of u ing the classes
compilation errors, or using as ertions to detect the violations at run
time (i.e., defensive progranuning).
Program to an inte,face, not to an implementation: Separate interface from
implementation. Client of a clas hould acce only the functionalities
of the clas via its interface. Implementation hould be hidden and
irrelevant to the client.
Separate functionalities that address differeflt concerns: If a cla
contains
components that addre di fferent concern , these components are
candidates for eparation from the original clas .
Minimize the inte1face: De ign the malle t po ible interface that provides
the needed functionality. Interface ize i detennined by the number of
method and their paran1eter . Large interface u ually indicate high
level of coupling with collaborating classes and high complexity.
304 • Design by Abstraction
FURTHER READINGS
EXERCISES
7.1 Extend the generic function plotter class class. The balls should bounce off each other
Plotter to plot the following functions: when they collide. Bonus: Allow the u ser to add
more balls.
(a) Y = .fi
(b) y = lnx 7.6 Enhance the latest version of the animation
applet Sort3 by adding the following capabil-
(c) y = tan x ities:
(d) y 3x 2 + 5x + 8
= x3 - (a) New sorting algorithms, such as insertion
7.2 Write a generic plotter applet that takes a pa- sort.
ramet.er func that represents a single-variable
(b) New display strategies.
polynomial function and plots the function. For
example, the input might be in the form 7.7 Refactor the bouncing ball applet in Exam-
ple 7 .2 to separate the ball object from the
animator. Extend the applet so that the object
being bounced around can be any shape or
7.3 Extend the generic multiple function plotter
image.
MultiPlotter to plot the following functions
in the same two-dimensional canvas:
(a) y = 5x
(b) y = 2x2 PROJECT
(c) y = 0.5x 3
(d) y = 0.2ex 7.1 Develop animation applets to animate graph
(e) y = ln x algorithms. Candidates include the following:
7.4 Write an applet that combines the applets in (a) Kruskal's and Prim's algorithms for min-
Exercises 5.7 and 5.8. It should support two imum spanning trees [Thomas, Corman,
display stra1.egies: a bar chart or a pie chart. and Rivest 1990). Animate the process of
The user should be able to choose either of the adding edges to the minimum spanning
two display strategies by setting a parameter. tree.
The display strategies are to be implemented (b) Dijkstra's algorithm for single- ource
by using the Strategy design pattern. shortest path [Thomas, Corman, and Rivest
7.5 Write an animated applet with two bouncing 1990]. Animate the process of discovering
balls by extending the DBAnimationApplet the edges on the shortest path.
•• ➔
Object-Oriented Application
Frameworks
CHAPTER OVERVIEW
In this chapter, we discuss the design of application frameworks and the use of design
patterns. We present three important frameworks in the Java Class Lib@ry- the collections
framework, the input/output framework, and the g@phical user interface (GUI) framework
using AWT and Swing.
8.1 .1 Characteristics
Extensibility
An application framework typically consists of a set of abstract classes and interfaces
to be extended and specialized. Its changeable aspects, also known as the hot spots of
the framework. are often represented as book methods. Custom applications use the
framework by extending or implementing the classes and interfaces in the framework
and by overriding the book methods to provide customized behaviors.
For example, a concrete animation applet, such as BouncingBall2, simply
extends the DBAnimationApplet class and overrides the hook methods paint-
Frame () and ini tAnimator O .
Inversion of Control
When we use a conventionaJ library of routines and classes, the applications usually
control the flow of execution. In other words, the applications are acting as the masters,
whereas the routines and classes in the libraries are acting as servants to provide
services. When we use an application framework, the control of the flow of execution
often resides in the framework, not in the applications. In other words, the framework
is acting as the master, whereas the applications are acting as servants to fill in the hot
spots.
For example, with the LinkedList class, the applications are in control, and the
LinkedList class simply provides services. That approach is quite different from
using the Applet class, which can be considered a mini-framework. The control
resides in the Applet .class and the applet context. A specific applet extends the
Applet class and ovemdes the hook methods, ini t (), start (), and the like, which
represent the hot spots of the applet framework.
ti
Completeness
The framework must provide a family of classes, united by a shared interface but each
employing a different representation. Developers can then select the classes having
the time and space semantics most appropriate to their given application.
Adaptability
All platform-specific aspects of the framework must be clearly identified and isolated.
Doing so permits local substitutions to be made.
Efficiency
Components must be easily assembled (efficient in tern1S of compilation re ources).
They must impose minimal run-time and memory overhead (efficient in execution
resources). And they must be more reliable than hand-built mechanisms (efficient in
developer resources).
Safety
Each abstraction must be type-safe o that latic a umptions about the behavior of
a class may be enforced by the compilation ys1em. Exception should be used to
signify nm-time violations of the contracl concerning the dynamic ernanlic of the
class. Rai ing an exception mu I nol corrupt lhe tate of lhe object that threw lhe
exception.
--
Simplicity
The framework must have a clear and consistent organization that makes it easy to
identify and select appropriate concrete classes.
Extensibility
Developers must be able to add new classes independently. At the same time they
must be able to preserve the architectural integrity of the framework.
To use application frameworks effectively, you must understand the interfaces, con-
ventions, and restrictions of a framework. In return, using well-designed frameworks
can significantly simplify the design and implementation of applications. The Java 2
Platform class library includes several well-designed and powerful application frame-
works. In the remainder of this chapter, we discuss the interfaces, conventions, and
designs of th~ following Java application frameworks:
A collection is an object that contains other objects, which are called the elements of
the collection. Based on their structures and capabilities, collections can be classified
in a few major categories known as abstract collections.
I. Not to be confused with the Container class in AWT. They are unrelated .
8.2 The Collections Framework • 309
There are four major types of collections: bags, sets, lists, and maps.
Bags
A bag is an unordered collection of elements that may contain duplicate elements.
Bags are also known as multi'sets. Bags are the least restrictive and most general
fonn of collections. In the Java collections framework, bags are represented by
the Collection interface. Bags are rarely used directly. More restrictive forms of
collections, such as sets and lists, are used much more often.
Sets
A set is an unordered collection of elements. No duplicate elements are allowed in
sets. In other words, inserting the same elements in a set twice is the same as inserting
the element just once. A set is denoted
where e 1, e2, ••• , en are the elements of the set. For example, the following is a set of
languages:
{ "English", "Chinese", "German" }
A variation of sets is sorted sets, whose elements are automatically sorted ac-
cording to a certain order. Sorted sets are also known as ordered sets. For example,
the following is an ordered set of languages sorted in alphabetical order:
In the Java collections framework, sets are represented by the Set interface, and
sorted sets are represented by the SortedSet interface.
Lists
A list is an ordered collection of elements. Lists are also known a sequences.
Elements in a list are indexed sequentially starting from 0. Duplicate elements are
allowed in lists. A list is denoted
where ei, e2 , ... , e,, are the elements of the list. For example, the following are three
different lists:
In the Java collections framework, list are repre ented by the List interface.
31 0 • Object-Oriented Application Frameworks
Maps
A map is an unordered collection of key-value pairs, denoted key 1-+ value. Map are
aJso known as functions, dictionaries, or associative arrays. The keys in a map muS t
be unique (i.e., each key can map to al most one value). A map is denoted
where ki, k2 , . .. , k,, are the keys and v 1, v2 , . . . , v11 are the values of the map. For
example, the following is a very small EngHsh-Chinese dictionary:
A variation of maps is sorted maps, whose elements are automatically sorted by keys
according to a certain order. Sorted maps are also known as ordered maps. In the Java
collections framework, maps are represented by the Map interface, and sorted maps
are represented by the Sorted.Map interface.
Figure 8.1
Collection Map
The abstract
collections.
SortedSet
8.2 The Collections Framework • 311
Although each kind of abstract collection has its unique characteristics, together
they have a lot in common. For example, all collections support insertion and deletion
of elements. It is simpler if the insertion and deletion of different collections can
be expressed by using the same methods and following the same conventions. More
important, different implementations of an abstract collection will all implement the
same interface. Thus, knowing the interface of an abstract collection is adequate for
use of all the implementations of an abstract collection. For example, the Set interface
can be implemented by using an array, a linked list, a tree, and so on. A user needs
to study only the Set interface in order to use all its implementations, and switching
from one implementation to another is easy. However, we often need to know about the
concrete implementation classes so that we c~ make informed decisions on the most
suitable implementation, depending on a number of factors, such as performance,
memory consumption, thread safety, and so on.
TABLE 1.1
Methods of Interface Collection
Method Description
The elements of a collection can be any objects (i.e., the elements can be 0 ~ any
reference type). Values of primitive types cannot be directly stored in collec~ions.
To be stored in a collection, the values of primitive types must be wrapped 111 an
object of a suitable wrapper class (see Section 4.4.9). In the methods contains C) •
containsAll (c) , remove (o) , removeAll (c), and retainAll(c) , the notion
of equality between two elements is defined by the equals() method of the class of
the elements.
Set set = new HashSet () ; II HashSet is the class that implements Set
set. add("foo "); II the set is { foo}
set. add (" bar") ; // the set is { foo, bar}
int n = set . size() ; JI n is 2
if (set.contains("foo")) //istrue
System .out .println("foo is in");
Method Description
The new methods and the methods with altered semantics in the List interface are
summarized in Table 8.3. In these methods, parameter o is of type Object, parameter
c is of type Collection, and parameters i and j are integers representing indices.
TABLE e·.3
Methods of Interface List
Method Description
Method Description
supported by maps. Maps are not sets, but they can be viewed as sets if so desired.
The Map interface provides the following views of maps:
■ The entry set view: The set of all the entries (i.e., key-value pairs) of the map.
■ The key set view: The set of all the keys contained in the map. It is a set because
keys are unique.
■ The value collection view: The collection of all the values contained in the map.
It is a collection rather than a set because different keys can be mapped to the
same value, in which case the value occurs multiple times in the value collection.
The methods of the Map interface are summarized in Table 8.4. l n these methods,
parameterk is of type Object and represents a key, parameterv is also of type Object
and represents a value, and parameter mis of type Map.
The keys and values of a map can be of any reference type. Here is an example
of using maps:
Each of the abstract collections can be implemented with various data structures
and algorithms. An implementation of the abstract collections is called a concrete
collection. The implementations may vary in many respects, including the following:
■ Whether the collection is bounded (i.e., has a fixed maximum size) or is un-
bounded (i.e., may grow and shrink dynamically as needed).
■ The time and space complexity of various operations, such as searching, inser-
tion, deletion, and iteration.
Common data structures used to implement the abstract collections include arrays,
linked lists, trees, and bash tables.
Each concrete collection is a class that implements the interface of an abstract
collection. This characteristic makes different implementations of the same abstract
collection interchangeable. For example, an array implementation of a list can be
switched to a linked list implementation without affecting the clients. The concrete
collectlons supported by the collections framework are summarized in Table 8.5. All
these concrete collections support unbounded collections. The collections can grow
and shrink dynamically as needed.
• The HashSet implementation stores the elements in a hash table. The HashSet
implementation is very efficient in insertion (add () ) and membership checking
(contains()). The time complexity of these operations i 0 ( 1). However,
the HashSet implementation doe not maintain any particular order among
the elements. The iteration order i unpredictable and determined by the hash
function of the elements.
■ The LinkedHashSet implementation i very imilar to the HashSet implemen-
tation, except the iteration order i predictable and u ually is the in ertion order.
■ The TreeSet implementation tore the element in a red-black tree, a form of
balanced binary tree. The TreeSet cla implement the SortedSet interface.
The elements are ordered ba ed on either their natural order or a u er-defined
order (see Section 8.2.5 [p. 324]). The TreeSet implementation i le efficient
than the HashSet implementation in in ertion and member-hip checking. The
time complexity of the e operation i O (log " ), where " i the ize of the set.
31 6 • Object-Oriented Application Frameworks
TAILE 1.5
Concrete Collections
whereas LinkedList implementation uses a doubly linked list to store the elements.
The main differences are as follows:
■ The ArrayList uses an array that is large enough to store all the elements. Often,
a large portion of the array is unused. The LinkedList wastes no space.
■ The ArrayList incurs a significant penalty of performance when the size of
the list exceeds the size of the array, in which case a new larger array must be
allocated and all the elements copied to the new array. The LinkedList incurs
no extra penalty on performance when the list grows.
■ The ArrayList is more efficient than the LinkedLi st for methods involving
indices, such as get O and set O methods. The time complexity of these oper-
ations is 0(1) for ArrayList and O (n) for LinkedList, where n is the size
of the list.
The Vector implementation is the same as the ArrayList implementation.
The main difference is that the Vector class supports additional "legacy methods"
that predate the collection framework. The Vector class is also thread-safe; i.e., its
objects are always well behaved even in the presence of multiple threads manipulat-
ing the same object (see discussion in Chapter 11). As a consequence, the Vector
implementation incurs a significant overhead in performance. When you are choos-
ing a concrete list implementation, the volatility of the list is the main factor. The
LinkedList implementation is more suitable for volatile lists (i.e., lists that grow
and shrink a lot). The ArrayList is more efficient for relatively stable lists.
Implementations for Map Four different implementations of the Map interface are
provided:
■ The HashMap implementation stores the entries in a bash table. The HashMap
implementation is very efficient in insertion (put() ) and mapping (get () ). The
time complexity of these operations is 0 (1). However, the HashSet implemen-
tation does not maintain any particular order among the entrie . The iteration
order is unpredictable and determined by the hash function of the key .
■ The LinkedHashMap implementation i very similar to the HashMap implemen-
tation, except the iteration order is predictable and usually is the in ertion order.
■ The Identi tyHashMap implementation is al o very imilar to the HashMap
implementation, except that the compari on on key is ba ed on reference identity
not object equality. This fact makes the implementation more efficient than the
HashMap implementation, when it i appropriate to u e identity compari on
instead of equality comparison.
■ The TreeMap implementation tore the entrie in a red-black tree. The TreeMap
class implements the SortedMap interface. The entrie are ordered based on
either the natural order or a u er-defined order of the key ( ee Section 8.2.5
[p. 324]). The TreeMap implementation i le s efficient than the HashMap im-
plementation insertion and mapping. The time complexity of these operations is
O(log 11), where II is the ize of the et.
--
PURPOSE
This example demonstrates the use of a set.
DESCRIPTION
This program reads a piece of text as input. It counts the total number of words and
the number of different words in the text input.
SOLUTION
This example is an extension of the program Words in Example4.7 [p. 127]. In Words,
we break the text input into words. In this example, we first add a simple counter to
count the total number of words in the text input. If a word occurs multiple times,
it will be counted multiple times. To count the number of different words in the text
input, we must remember the words that have already occurred. A set serves this
purpose well. We use a HashSet to store the different words in the text input. Each
word occurs only once in the set even if it occurs multiple times in the input text.
CIIIIII Ccnm.tVords
import java.util . •;
import java. io . •;
public class CountWords {
static public void main(String[] args) {
Set words ~ new HashSet();
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
String delim • " \t\n.,: ;? 1- /()[]\"\ ' ";
Stri ng l i ne ;
int count = O;
try {
while ((line= in . readLi ne()) != nul l ) {
St ringTokenizer st= new Str i ngTokenizer( l i ne , delim) ;
~ -
while (st.hasMoreTokens()) {
count++;
words.add(st.nextToken().toLowerCase() );
}
}
} catch (I0Exception e) {
e.printStackTrace();
}
System.out.println( 11 Total number of words: 11 + count);
System.out.println( 11 Number of different words: 11 +
words. size()) ;
}
}
Using President Lincoln's Gettysburg Address as the input, we have the following
output:
The Iterator pattern is used to provide a uniform way to iterate through different
concrete collections (i.e., polymorphic iteration). Two iterator interfaces are defined:
Iterator and Listiterator. Instances of Iterator suppon traversal of collec-
tions in one direction (forward), and instances of Listiterator suppon traversal
of lists in forward and backward directions. The methods of the Iterator interface
are summarized in Table 8.6.
Method Description
TABLE 8.7
Methods of Interface Listlterator
Method Description
add ( o) Inserts the element o into the list at the current position
hasNext O Returns true if this list iterator has more elements when
traversing the list in the forward direction
hasPrevious O Returns true if this list iterator has more elements when
traversing the list in the reverse direction
next () Returns the next element in the list
nextlndex O Returns the index of the element that would be returned by a
subsequent caU to next 0
previous O Returns the previous element in the list
previous Index() Returns the index of the element that would be returned by a
subsequent call to previous()
remove O Removes from the list the last element that was returned by
next() or previous()
set (o) Replaces the last element returned by next () or previous ()
with the element o
8.2 The Collections Framework • 321
interface. Instances of concrete iterators are obtam·ed through one of the followmg
• th .
me th ods defin ed m e Collection and List interfaces, respective · 1y:
■ Defined in the Collection interface:
Iterator iterator()
The interface Map. Entry is defined inside the Map interface as a ne ted interface.
The elements in the entry set view of maps are instances of Map . Entry. The method
of the Map. Entry interface are summarized in the following table. In these method ,
parameter v is of type Object and represents a value in a key-value pair.
Method Description
PURPOSE
This example demonstrates iterating through the entry set of a map.
DESCRIPTION
This program reads a piece of text as input. It counts the number of occurrences of
each word in the text input.
SOLUTION
In order to count the number of occurrences of each word, we need a map that maps
each word to the number of its occurrences. To print out the result, we first obtain the
entry set view of the map. We then iterate through the entry set and print out e ach
entry.
The values in the map are instances of the following classes:
C..Count
public class Count {
public Count(String word, inti) {
this . word= word;
this.i = i;
}
ClallVordFr
import java .util . •;
import java . io . •;
public class WordFrequency {
static public void main (String[)args ) {
Map words = new Has hMap( );
8.2 The Collections Framework ■ 323
Using President Lincoln's Gettysburg Address as the input, we have the following
output:
devotion 2
years 1 men 2
civil 1 remember 1
place 1 who 3
gave 2 did 1
they 3 work 1
struggled 1 rather 2
fathers 1
Note that the words in the output are not in any particular order. •
324 • Object-Oriented Application Frameworks
An order, more precisely a partial order, is a binary relation between two objects
that is transitive. Let -< denote a partial order; then, for any three objects a, b,.ao<l c •
a -< b and b -< c implies a -< c. If a partial order ensures that for any two ~bJects a
and b, a-< band b-< a implies a= b, then it is called a total order . A part1al_order
that is not a total order is called a strictly partial order. A sorted collection is one
that orders its elements according to a certain order. Sorting ananges the elements of
a collection so that they are ordered according to a certain order.
There are two ways to define orders on objects:
1. Each class can define a natural order among its instances by implementing the
Comparable int~rface.
2. Arbitrary orders among different objects can be defined by comparators, 01;
classes that implement the Comparator interfa~e.
Toe compareTo O method compares the receiving object this with the param-
eter o. The result of the comparison is an integer. The contract of the method is
The compareTo O method must proper1y define a total order. For any two objects
a and b,
Also, the definition of the compareTo O method must be consistent with the
definition of the equals () method. For any two objects a and b,
The compare O method compares the two parameters. The result of the com-
parison is an integer. The contract of the method is
Also, the definition of the compare () method must be consistent with the
definition of the equals() method in the class of objects being compared. For any
two objects a and b, and a comparator c,
c . compare (a, b) is O if and only if both a . equals (b) and b. equals (a) are
true ;
a. equals (b) is true implies c . compare (a, b) is O; and
c. compare (a, b) > Oimplies c. compare (b, a) < 0, and c . compare (a, b) < 0
implies c. compare (b, a) > 0.
Sorted Collections
There are two sorted abstract collections: SortedSet and SortedMap. The Sort-
edSet interface extends the Set interface. All the method of the Set interface
are inherited. The new method in the SortedSet interfaces are summarized in the
following table. The parameters of the methods, o, ol, and o2, are all Objects rep-
resenting elements.
326 ■ Object-Oriented Application Frameworks
Method Description
The Sorted.Map interface extends the Map interface. All the methods of the Map
interface are inherited. The new methods in the Sorted.Map interface are summarized
in the following table. The parameters of the methods, k , kl, and k2, are all of type
Object representing keys.
Method Description
comparator () Returns the comparator associated with this sorted map, or null
if the natural order is used
firstKey() Returns the first (lowest) key currently in this sorted map
headMap (k) Returns a map view of the portion of this sorted map whose
keys are strictly less than k
lastKey() Returns the last (highest) key currently in this sorted map
subMap(k1, k2) Returns a map view of the portion of this sorted map whose
keys range from k1, inclusive, to k2 , exclusive
tailMap(k) Returns a map view of the portion of this sorted map who e
keys are greater than or equal to k
• SC() creates a sorted collection that is sorted according to the natural order of
the elements.
• SC ( Comparator c) creates a sorted collection that is sorted according to the
order defi ned by the specified comparator.
8.2 The Collections Framework • 327
PURPOSE
This example demonstrates the use of a sorted map ordered according to the natural
order of its keys.
DESCRIPTION
This program reads a piece of text as input. It counts the number of occurrences of
each word in the text input and prints out the results as a list sorted according to the
alphabetical order of the words.
SOLUTION
This is a variation of Example 8.2 [p. 322). The words are the keys of the map, and
the alphabetical order of words is the natural order of String. So we can imply use
a TreeMap, which by default maintains its entries according to the natural order of
its keys.
CIIIIIVordFre
import java.util.•;
import java . io . •;
public class WordFrequency2 {
static public void main (StringO args) {
Hap words • new TreeHap();
String delim =" \t\n . ,: ;? !-/O [] \"\'" ;
BufferedReader in= new BufferedReader(
new InputStreamReader (System. in));
String line, word;
Count count;
try {
while ((line= i n.readLine () ) != null) {
Stri ngTokenizer st= new StringTokenizer(line, delim);
while (st .hasMoreTokens() ) {
word= st.nextToken (). toLowerCase ();
if (words . containsKey (word)) {
count= (Count ) words . get (word);
count . i++ ;
} else {
words.put (word, new Count (word , 1));
}
}
}
} catch (I0Exception e) {
e.printStackTrace();
}
Set set= words . entrySet ();
Iterator iter = set . iterator () ;
6
328 ■ Object-Oriented Application Frameworks
a 7
above 1 whether 1
add 1 which 2
address 1 who 3
advanced 1 will 1
ago 1 work 1
all 1 world 1
years 1
~
PURPOSE
This example demonstrates the use of a sorted map ordered according to a user-defined
order on the keys.
DESCRIPTION
This program reads a piece of text as input. It counts the number of occurrences of
each word in the text input and prints out the results as a list sorted according to the
reverse alphabetical order of the words .
SOLUTION
This is another variation of Example 8.2 [p. 322]. To sort the entries according to
an order other than the natural order of the keys, we need to define a user-defined
order. An instance of TreeMap can be instantiated by specifying the user-defined
comparator in the constructor.
The following program contains the comparator for the reverse alphabetical order
of strings. To obtain the reverse alphabetical order, we simply negate the sign of the
result of the compareTo O method of the String class.
8.2 The Collections Framework • 329
Again, note that the class WordFrequency3 is also nearly identical to class
WordFrequency in Example 8.2 [p. 322); the exception is the line in boldface,
where a user-defined comparator is provided as the parameter of the constructor of
the TreeMap. Using President Lincoln's Gettysburg Address as the input, we obtain
the following output:
years 1
world 1 all 1
work 1 ago 1
will 1 advanced 1
who 3 address 1
which 2 add 1
whether 1 above 1
a 7
Utilities on Collections
Besides the sorted collections, the collection framework also provides a set of useful
utilities and algorithms related to ordering and sorting. They are provided as static
methods of the Collections class. These methods are summarized in Table 8.8.
Parameter 1 is of type List, parameter c is of type Collection, parameter k is of
type Object, and parameter comp is of type Comparator.
PURPOSE
This example demonstrates the use of sorting utilities to sort collections according to
user-defined orders.
DESCRIPTION
This program reads a piece of text as input. It counts the number of occurrence of
each word in the text input and prints out the results as a list sorted by the freq uencies
of occurrences of the words.
SOLUTION
This is another variation of Example 8.2 [p. 322). In this case, the desired order is not
based on keys but on values, so sorted maps offer no help. We use a HashMap to tore
the word counts. After all the words have been counted, we obtain a collection view
of the values contained in the map and then sort the value collection according to a
user-defined comparator CountComparator.
The comparator is used to sort the frequency list.
8.2 The Collections Framework ■ 331
TAILI •• I
Utility Mdhods of the Collections Class
Method Description
sort (1) Sorts the list 1 according to the natural order of the
elements
sort (1, comp) Sorts the list 1 according to the order defined by the
comparator comp
binarySearch(l, k) Searches for the element kin the list l, using the
binary search algorithm. The list 1 is sorted according
to the natural order of the elements. The return value
is the index of element k if it is present in the li t, or
(insertion point) - I. The insertion point is the index
at which the element should be inserted.
binarySearch (1, k, comp) Same as preceding, except that the li t 1 is sorted
according to the order defined by the comparator comp
min ( c) Returns the minimum element in the collection c
according to the natural order of the elements in the
collection
min ( c, comp) Returns the minimum element in the collection c
according to the order defined by the comparator comp
max ( c) Returns the maximum element in the collection c
according to the natural order of the elements in the
collection
max ( c, comp) Returns the maximum element in the collection c
according to the order defined by the comparator comp
Com · :!:'r.!..
tor for word count: CoutC,!! ,.........
· _.
publi c class CountComparator implements Comparator {
publ ic int compare(Object ol, Object o2) {
if (ol != null &&
o2 != null &&
ol instanceof Count &&
o2 instanceof Count) {
Count cl= (Count) ol;
Count c2 = (Count) o2;
return (c2.i - cl . i);
} else {
return O;
}
}
}
••
,
I Class WordFrequency4
import java . util.•;
import java.io . •;
public class WordFrequency4 {
static public void main(String[) args ) {
Map words= new HashMap ();
String delim =" \t\n . ,:;?!-/()[)\ 11 \ ' " ;
BufferedReader in= new BufferedReader (
new InputStrearnReader (System . in) );
String line, word;
Count count;
try {
while ((line= in . readLine ()) != null) {
StringTokenizer st= new StringTokenizer (line, delim);
while (st.hasMoreTokens()) {
word= st .nextToken () .toLowerCase ();
count= (Count) words . get(word);
if (count== null) {
wor ds . put(word, new Count(word, l) );
} else {
count.i++;
}
}
}
} catch (IOException e) {
e.printStackTrace() ;
}
List list= new ArrayLi s t(words . val ue s ());
Collections . sort(lis t, new CountComparat or()) ;
I terat or iter s list . iter at or () ;
while (iter .hasNext()) {
count= (Count) iter.next();
word = count.word ;
System . out .println(word +
(word.length() < 8 ? 11 \t\t" "\t ") +
count . i);
}
}
}
Using President Li ncoln's Gettysburg Address as the input, we have the following
output:
the 13
that 12 consecrate 1
we 10 world 1
here 8 consecrated 1
to 8 remember 1
a 7 did 1
and 6 work 1
fathers 1
8.3 The Graphical User Interface Framework-AWT and Swing • 333
Java provides an extensive framework for building high-quality graphical user inter-
faces (GUI). The graphical user interface framework i part of the Java Foundation
Classes (JFC). We focus on two important packages in the JFC: the Abstract Windows
Toolkit (AWT) and Swing.
The Java GUI framework consists of several categories of classes. The main
categories are the following:
GUI components: Components, also known as widgets, are the building blocks
of the visual aspect of graphical user interfaces. Each GUI component is
characterized by its its visual appearance, the look, and its behavior pattern
in response to user input, the feel. Each type of GUI component is defined
by a GUI component class. Examples of the GUI component classes include
Button, Label, Checkbox, Scrollbar, Frame, and Dialog.
Layout managers: Layout managers define strategies for laying out GUI compo-
nents in windows. Commonly used layout managers include FlowLayout
and BorderLayout.
Events and event listeners: Events represent user input or action . Each event
class represents a particular type of user input, such as the KeyEvent cla
for keyboard input and the MouseEvent class for mouse actions. Each event
class is associated with an event listener class responsible for handling this
type of event. For example, the KeyListener class is associated with
the KeyEvent class, and the MouseListener cla is associated with the
MouseEvent class.
Graphics and imaging classes: The e clas es allow component to draw their
visual appearances and include graphics (Color, Font, Graphi cs, etc.),
geometry (Point, Rectangle, Dimension. etc.), and imaging (Image.
Icon, etc.).
The Abstract Windows Toolkit (AWT) package provides the ba ic upport for
building graphical u er interface . The Swing package i an extension of AWT,
which provides extensive support for building ophi ticated-looking and high-quality
graphical user interfaces.
Figure 8.2
Checkbox r------,
Choice
Label Frame
List
TextComponent . . - - - -
java.applet.Applet
TextArea TextField
components. The subclasses of Component class are divided into two groups: primi-
tive components, which do not contain other components, and con tain ers, which may
contain other primitive components and containers.
The Container class is an abstract class that defines the characteristics and be-
haviors common to all the containers. In Figure 8.2, the left branch of the Component
class consists of the primitive components, and the right branch of the Component
class consists of the containers. Using containers, we can construct graphical user
interfaces by organizing components into tree structures, in which the primitive com-
ponents are the leaves and the containers are the interior nodes. Furthermore, as both
primitive components and containers are subclasses of the Component class, they can
be treated uniformly.
TABLE 8.9
Component and Container Classes in AWT
I Frame
Panel
Window
Top-level window with a title and a border
Borderless, titleles , and transparent container
Borderless and titlele top-level window
Swing counterparts
of AWT compo-
JComponent
nents.
JButton
Jlabel Window
JComboBox ,____ _.
Jlist
JTextComponent
JWindow
JTextField JTextArea
system. For example, a Java GUI application using Swing can have a Motif look even
when it is running on a Win32 platform. This capability is known as the pluggable
look and feel.
The Swing package consists of several hundred classes and numerous subpack-
ages, of which we cover only a few. The most straightforward part of Swing consists
of classes with counterparts in AWT. These classes are shown in Figure 8.3, with the
shaded boxes being classes in AWT. The names of Swing component classes begin
with a prefix J followed by the name of their counterparts in AWT.2 The subclasses of
JComponent represent lightweight components. Note that JWindow, JFrame, and
JDia1og are not lightweight components. The differences between these Swing com-
ponents and their AWT counterparts are summarized in Table 8. I 0.
The design of the GUI components hierarchy illustrates a commonly used design
pattern : the Composite pattern.
TAILll.10
Component and Container Classes in Swing
• design
The structure of the Composite • pattern 1s · tl1e following diagram:
· shown rn
Component *
f
I
Leaf Composite 0-
operation() operation()
add(Component)
remove(Component)
getChild(int)
Each container has a layout manager, which handles the layout of the components
contained in the container. Several layout managers are provided in the JDK, as
illustrated in Figure 8.4. Each layout manager defines a layout strategy. One of the
advantages of using layout managers is that we do not have to specify the absolute
coordinates and dimensions of each component. The components in the container
are displayed based on their relative positions. The relative positions of components
can be specified explicitly with positional constraints or implicitly by the order in
which they are inserted in the container. The positions and sizes of the components
are computed by the layout manager based on the dimensions of the container and
automatically adjusted whenever the dimensions of the container change, which is
usually caused by resizing the top-level window.
8.3 The Graphical User Interface Framework-AWT and Swing • 339
Flow layout
GridBaglayout
Gridlayout
We discuss and illustrate the use of several commonly used layout managers:
FlowLayout, GridLayout, and BorderLayout. We also demonstrate how to do
customized layout without using the layout managers.
Method Description
Flow Layout
The flow layout is the most straightforward layout trategy. It arrange the components
in a left-to-right flow, in the order in which they were in erted in the container. If the
container is not wide enough for all the component , it break the flow into several
lines. Each line i centered by default, and each component i ized to its nat11rc1l size.
340 • Object-Oriented Application Frameworks
A flow layout manager can be created by using one of the following constructors of
the FlowLayout class:
Constructor Description
FlowLayout (al ign, hGap, vGap) Creates a flow layout manager with the
alignment set to align and the horizontal
and vertical gap set to hGap and yGap,
respectively
FlowLayout (align) Creates a flow layout manager with the
alignment set to align and the horizontal
and vertical gaps set to the default value
FlowLayout O Creates a flow layout manager with the
alignment and the horizontal and vertical
gaps all set to their respective default
values
The default alignment is centered. The horizontal and vertical gaps are the gaps
between the adjacent components, specified in pixels. The defaults are 5 pixels for
both the horizontal and vertical gaps.
PURPOSE
This example demonstrates the use of the flow layout and the creation of buttons.
DESCRIPTION
The Applet class is a subclass of Panel, so the drawing area of an applet is actually
a container. We can add any component to the panel. Here, we simply create and
add six buttons to the panel. The results are shown in Figure 8.5. The layout shown in
Figure 8.5(a) is the result of using the flow layout in a panel that is 400 pixels wide and
50 pixels high. The layout shown in Figure 8.5(b) is the result of using the same flow
layout in a panel that is 100 pixels wide and l 20 pixels high. Note the correspondence
between the order of insertion and order of buttons in the layouts.
8.3 The Graphical User Interface Framework-AWT and Swing ■ 341
Figure 8.5
~ Applet Viewer: Flow.class [jjiiijlr
■l ~
Applet
Flow layouts.
I- Java I C++ I Pert I Ada I ~ I Elffef I
!Applet started.
(a)
I- .
i
□R
l Java I
C++
I
;I Pert I I Ada
\II Smallalc I
11 EHW I
,,!•Applet started.
(b)
SOLUTION
A FlowLayout object is created first and is set as the layout manager of the container
(i.e., the Panel object) using the setLayout () method of the Container class. A
button can be created as follows:
where label is the label of the button. Each button is added to the container using the
add () method of the Contai ner class. Since the order of the components in a flow
layout is determined by the insertion order, no positional constraints are necessary
for the add () method when using FlowLayout.
Flow laY9_!1t a
import java . awt.•;
i mport java . applet . Applet ;
public class Flow extends Applet {
public Flow() {
setLayout(new FlowLayout());
add(new Button ("J ava")) ;
add(new Button("C++" ));
add(new Button("Perl ") ) ;
add (new Button ( "Ada")) ;
add (new Button ( "Smalltalk" )) ;
add (new Button( "Eiffel") ) ;
}
}
34 2 • Object-Oriented Application Frameworks
Grid Layout
The grid layout arranges components in a rectangular grid, and all compo~ents are
given the same size. The components can be stretched vertically and honzontally,
if necessary, to fill the entire space of the container. A grid layout manager can be
created by using one of the following constructors of the GridLayout class:
Constructor Description
When the first two constructors are used, r and c must be nonnegative integers.
One of them, but not both, can be 0. If r is 0, the grid may have any number of rows,
depending on the number of components in the container. Similarly, if c is 0 , the grid
may have any number of columns, also depending on the number of components in
the container.
PURPOSE
This example demonstrates the use of the grid layout.
DESCRIPTION
This program is similar to the one in Example 8.6. The main difference is that a
grid layout manager is used here. The numbers of rows and columns of the grid are
specified as the parameters of the applet.
Figure 8.6 shows the effects of grid layout in a panel with six buttons. The layout
shown in Figure 8.6(a) is the result of specifying 1 row and O column, or ( 1, O).
The layout shown in Figure 8.6(b) is the result of specifying 3 rows and 2 columns,
or (3, 2). The layout shown in Figure 8.6(c) is the result of specifying O row a nd J
column, or (0, l ).
SOLUTION
First, the numbers of rows and columns of the grid are determined based on the
values of two parameters specified in the <applet> tag: row and col. Second, a
8 ·3 The Graphical User Interface Framework-AWT and Swing • 343
Figure 8.6
!MJ®$19iMMifflGG%t
Applet
- ; !g1fx1
Grid layouts. r,..-
1• l .,--- - ..,
Java C++ Perl Ada Eiffel
1
I
Smallalk
I
!Applet started.
-
(a)
Applet Applet
Java
Ada C++
1,Applet started
Ada
(b)
Smalla1k
l
Applet started.
(c)
GridLayout object is created with the given number of row and column and i set
as the layout manager of the container. Third, a number of button are inserted into
the container. The position of components in a grid layout i al o determined by the
order of insertion, so no positional con traint are nece ary for the add () method
when using GridLayout.
Border Layout
The border layout is one of the most versatile layout managers. It arranges as many as
five components in five positions identified as North , South, East, West, and Center,
as illustrated in Figure 8.7. The North and South 9omponents are placed at the top
and bottom of the container, respectively. They are set to their natural heights and
may be stretched horizontally to fill the entire width of the container. The East and
West components are placed at the right and left sides of the container, respectively.
They are set to their natural widths and may be stretched vertically to fill the space
between the North and South components. The Center component may be stretched
horizontally and vertically to fill the space left in the center. If one or more of the
components, except the Center component, are absent, the remaining components
will be stretched vertically and/or horizontally to fill the space left by the missing
components. A border layout manager can be created by using one of the following
constructors of the BorderLayout class:
Method Description
PURPOSE
This example demonstrates the use of the border layout.
DESCRIPTION
This applet uses a border layout manager and simply inserts a button in each of the
five positions.
SOLUTION
The buttons are inserted using the add() method with a positional con traint.
PURPOSE
This example demonstrates the nesting of containers and creation of choices and
labels.
DESCRIPTION
This applet yields the layout shown in Figure 8.8. The outer panel contain two nested
panels and uses a border layout manager. Three buttons are inserted in the North, East,
and West positions of the outer panel. The center position contains another panel,
which also uses a border layout manager. A button is inserted in each of the five
positions of the panel at the center. The South position of the outer panel contains yet
another panel, which uses a flow layout manager. A button, a choice, and a label are
inserted in the pane] in the South position.
IOUlh
Help I! i:J
one This is a message bar.
Applet started.
8.3 The Graphical User lntertac~ Framcwork-AWT and Swing • 34 7
SOLUTION
Nested : NeatedPanela
import java .awt .• ;
import java.applet .Applet ;
public class NestedPanels extends Applet {
public NestedPanel s () {
II set up the center panel
Panel center= new Panel ();
center.setLayout (new BorderLayout ());
center.add (new Button(" south" ), BorderLayout .SOtrrH);
center.add (new Button("north" ) , BorderLayout .NORTH);
center.add (new Button("east"), BorderLayout . EAST) ;
center . add(new Button("west "), BorderLayout .WEST);
center .add(new Button("center" ), BorderLayout .CENTER);
II set up the south panel
Panel south= new Panel ();
south.setLayout(new FlowLayout ());
south . add(new Button("Help") );
choice= new Choice ();
choice.addltem("one");
choice.addltem("two");
choice . addltem("three") ;
choice .addltem("four");
choice.addltem("five" ) ;
south . add(choice) ;
messageBar = new Label("This is a message bar .");
south.add(messageBar) ;
II set up the outer panel
setLayout(new BorderLayout ());
add(new Button ("North" ), BorderLayout .NORTH);
add(new Button("East" ) , BorderLayout . EAST);
add(new Button ("West" ), BorderLayout .WEST);
add(south, BorderLayout .SOUTH) ;
add(center, BorderLayout .CENTER);
}
GUI components communicate with the rest of the applications through events, which
represent user inputs or actions. The source of an event is the component from which
the event is originated. A listener of an event is an object that receives and processes
the event. Events are classified into different types, each of which is represented by
an event class, which is a subclass of AWTEvent . The class hierarchy of event classes
is shown in Figure 8.9. Each type of event is associated with a listener interface,
which is implemented by the listeners of that event type. The listener interfaces and
their associations with event classes are also shown in Figure 8.9. Listeners must be
registered to their respective sources before they can receive events from their sources.
Event handling in Java programs involves the following steps:
■ Determine the types of events to be handled and their associated listener inter-
faces . For example, to handle button clicks, the event class is the ActionEvent
and the associated listener interface is ActionListener.
■ Define listener classes that implement the listener interfaces and implement all
the methods of the interfaces. For example, to handle button clicks, the listener
class must implement the ActionListener interface.
■ Create instances of the components, which are the sources of the events. For
example,
i---1
I
AdjustmentListener I·•• .... --•---•.. •••.. •.. •······ AdjustmentEvent
I
I
ComponentListener · · · ·· ·· · ·.. · · · -·· · .... ·· · .... · - ComponentEvent
'--4 ComponentAdapter I
I
I
1--
1
ContainerListener ContainerEvent
:
1
- - -I ContainerAdapter \
I
1
\ - - ~~- -
F_
oc_u_s_A_d_a_p_te_r_ ~\ lnputEvent
I
t -4.____ K_ey_A_da
_p_te_r _ __.\
: t _-\~__ M_o_u_se_A
_d_a_p_t_
er_ __,\ '--r-------
,--
1
MouseMotionListener · · · · ·-· · · · · -· · · · ·· · PaintEvent
I
: '---I MouseMotionAdapter \
I
\- - -1~-~W
_ in_d_o_w_L_is_t_e_
ne_r_ ~\ · .. ·· · ·· · -· .. · · ...... · · · .___W
_ in_d_o_w_E_v_e_n_
t ___.
\
I
~ - -\.___W_in_d_o_w_A_d_a_p_te_r_......,\
\-
I
- -\ Item Listener ~ · .. · ··· ·· · ·· ···.... · ·.. · -·· .. ·· ItemEvent
.___ __ _ __ ~
■ Create instances of the li tener , and regi ter the li teners to their sources. For
example, to handle button click u ing MyButtonHandler u e
A listener may listen to several sources, and a source can have multiple listeners.
Continuing with the preceding example, we can create another button, button2, and
let the listener handler handle clicks on both buttons:
1. When an event is triggered, the Java run time first determines its source and type.
2. If a listener for this type of event is registered with the source, then an event
object is created.
3. For each listener that listens to this type of event, the Java run time invokes the
appropriate event-handling method of the listener and passes the event object as
the parameter.
The interaction among the source, its listeners, and the event objects is shown in
Figure 8.10.
Figure 8.10
The event
handling.
<<create>>
aEvent
notify(aEvent)
query()
PURPOSE
This example demonstrates event handling.
DESCRIPTION
This program extends the Nested.Panel class presented in Example 8.9 by adding
event handling. When one of the buttons is clicked or one of the items in the choice
is selected, a message is displayed in the message bar, which is a label. The class
NestedPanels2 listens for both types of events and listens to all the buttons and the
choice contained in the nested panel.
SOLUTION
The event class for button click is ActionEvent, and the associated listener interface
is ActionListener. The event class for choice item election i ItemEvent, and
the associated listener interface is ItemListener.
Event listener classes are usually very small classes. To avoid unnecessary pro-
liferation of small classes and files, event listener classes are commonly defined as
nested classes. Furthennore, being nested classes also gives the listener classes access
to the nonpublic fields of the enclosing class, which is often needed for handling the
events.
PURPOSE
This example demonstrates the use of nested classes as event listener classes.
DESCRIPTION
This program behaves the same as the program in Example 8.10. It uses two nested
classes: one handles the button cli°cks, and the other handles the choice item selections.
SOLUTION
Neaed panels applet lllinl nested class event listeners: NestedPanels_3
import java.awt . •;
import java . awt.event . • ;
publi c class NestedPanels3
extends NestedPanels {
public NestedPanels3() {
super();
ChoiceEventHandler cHandler = new ChoiceEvent Handler () ;
I , f • 1
PURPOSE
This example demonstrates factorization by delegation and the u e of buttons and
choices to control animation .
354 • Object-Oriented Application Frameworks
DESCRIPTION
This is an enhancement of the bouncing ball applet in Example 5.4 using Swing
components. The layout of the applet is shown in Figure 8.11. Three GUI component
are added to the original applet to control the animation:
SOLUTION
In the original bouncing ball applet, the animation occupies the entire viewing area.
The program consists of only one GUI component, the top-level panel. All methods
dealing with animation belong to the applet class. In the reusable animation applet
class AnimationApplet in Example 7.1 [p. 256], we assumed that the animation
component occupies the entire viewing area. However, in the present case, the ani-
mation component occupies only a portion of the viewing area. We need a reusable
animation component that is not tied to the top-level panel of an applet but that can be
associated with any GUI component. In this case, it is better to use delegation to refac-
tor the animation component so that the concrete animation class does not have to be
the subclass of the reusable animation class. This approach permits multiple instances
of the reusable animation class in a single program. The structure of the delegation-
Figure 8.11
Applet
The bouncing ball
applet with con-
trols.
------~;...._-----II
start
[ Applet started.
stop red
S.3 The Graphical User Interface Framcwor1<-AWT and Swing • 355
based reusable animation clas is shown in Figure 8.12. The implementation is shown
in the following program:
Del __ tor:bi■ator
import java.awt.•;
based reusable
Comp
animation class. Animator K>------1 MyComponent
start()
stop()
run()
setDelay()
getDelay()
356 • Object-Oriented Application Frameworks
Figure 8.13
I Applet
L;::.
The structure of
the bouncing ball
Animator
applet with con-
trols. I BouncingBall3 ,~ animator
start()
k>comp
iComponent
L;::..
I
()
stop()
canvas run()
setDelay()
getDelay() I JPenel j
L;::.,
BouncingBallCanvas
paint()
In the bouncing ball applet with controls, the animation component is the Bounc-
ingBallCanvas class, which extends the JPanel class in Swing. The JPanel class
supports double-buffering. The top-level applet is the BouncingBall3 class. The
structure of the entire program is shown in Figure 8.13.
The BouncingBall3 class contains two nested classes for handling the events
from the start and stop buttons and the color-choice box.
A GUI application starts with a frame. Aframe is a top-level window with a border,
a title bar, and control buttons located at the upper comers of the frame. The main
work area of a GUI app is usually contained within a frame. A dialog is a pop-up
window that usually appears for only a short period of time and requires the immediate
attention of the user. Dialog windows are used for displaying messages or getting
input.
DESCRIPTION
The frame of the online ordering app contains two components: an image label, which
consists of an image and a text, and the Order button. When the Order button is pushed,
a dialog box pops up. The dialog box bas three compartments, as shown in Figure 8.14.
■ The top compartment contains text labels and text fields. The layouts of the labels
and the fields are not handled by a layout manager. Rather, a customized layout
is specified, using the absolute position and dimension of each component.
■ The nuddle compartment has a titled border with the title Credit Card. It contains
three check boxes that are exclusive.
■ The bottom compartment has an etched border. It contains the OK and Cancel
buttons. An order is completed when the OK button i pushed, and the order
information will be printed to the tandard output.
The structure of the online-ordering app i hown in Figure 8.15. The Order cla 1
the top-level class of the GUI application. It extends the JFrame class.
SOLUTION
The onllne shopein& app: Order
import java.awt. • ;
import java . awt .event.•;
import javax . sw ing . •;
public class Order extends JFrame {
publi c Order(String i mageFile) {
setTitle (imageFile);
getContentPane() .setLayout(new BorderLayout());
I con image = new Imageicon(imageFile) ;
360 ■
Object-Oriented Appllcatlon Frameworks
Figure 8.14 Frame control menu Frame title bar Frame control buttons
- - - -~ -
The online
ordering fonn.
E-Mail Imowen@aol.com
~~~ ~~- = ====~
CredttCirc:1 -=-....£.......
rettvtu !
~ !IMaiteiCarc:1 (ii1 Dlacover
1
p
JLabel center=
new JLabel( "To order the item shown above, click the button",
image,
SwingConstants.CENTER);
center . setHorizontalTextPosition(SwingConstants.CENTER);
center . setVerticalTextPosition(SwingConstants . BOTTOM);
JPanel bottom= new JPanel();
JButton orderButton = new JButton("Order") ;
bottom.add(orderButton);
orderButton . addActionListener(makeOrderHandler());
getContentPane() . add(center, BorderLayout . CENTER) ;
getContentPane().add(bottom, BorderLayout.SOUTH) ;
addWindowListener(new AppCloser());
}
ActionListener makeOrderHandler() {
return new OrderHandler();
}
The structure of
the onllne
shopping app. Order K>----1 DialogPanel
ActionListener
362 ■
Object-Oriented Application Frameworks
The OrderHandler is the listener class of the Order button. The first time the
Order button is pushed, an instance of OrderDialog, which represents the order
dialog box, is created with the invocation of
new OrderDialog(Order . thi s )
The order dialog box becomes visible when dialog. show () is invoked. The order
dialog box is created only once. Subsequently, pushing the Order button causes the
order dialog box to reappear by invoking dialog. show () .
The main () method creates an instance of the order frame. The order frame
becomes visible when frame. show O is invoked.
The OrderDialog class represents a dialog box that pops up when the Order
button in the order frame is pushed. It contains the OK and Cancel buttons and the
order information panel.
DialogPanel dialogPanel;
public OrderDialog(JFrame owner) {
super(owner, true) ;
setTitle("Order");
okButton = new JButton("Ok");
cancelButton = new JButton ("Cancel");
ButtonHandler bHandler = new ButtonHandler ();
okButton.addActionListener(bHandler);
cancelButton.addActionListener(bHandler);
bottom= new JPanel();
bottom.add(okButton);
bottom.add(cancelButton);
bottom . setBorder(BorderFactory.createEtchedBorder() ) ;
dialogPanel = new DialogPanel();
getContentPane() . setLayout(new BorderLayout () );
getContentPane() . add(bottom, BorderLayout.SOUTH);
getContentPane ().add(dialogPanel, BorderLayout .CENTER) ;
}
The DialogPanel class represents the order-information panel that contains text
fields for the customer's name, address, and e-mail. Each text field is accompanied
by a text label. It also contains three check boxes for choosing a credit card. The
selection of the check boxes is exclusive; i.e., only one of them can be selected. The
check boxes are contained in another nested panel.
The constructor creates instances of the text fields and labels. Swing components
may have borders. The panel for check boxes has a titled border. The three check boxes
are added to the same button group, so that they are exclusive.
The reset() method clears the text fields so the same dialog panel can be used
by the next customer.
The ButtonHandler cla s i the event Ii tener cln for the buttons in the order
dialog box. If the OK button i pu hed, the order infom1ation is printed to the tand ard
output. When either the OK or the Cancel button is pushed, the text fields are cleared,
and the dialog box is closed.
366 • Object-Oriented Application Frameworks
There are two kinds of streams: byte streams and character streams. Byte streams
support reading and writing data of any type, including strings, in the binary format.
Character streams support reading and writing of text, using locale-dependent char-
acter encodings.
The most basic and primitive stream 1/0 capabilities are declared in two abstract
classes: InputStream and OutputStream. They support reading and writing of a
single byte and a sequence of bytes in an array. The methods for reading and writing
are summarized in the following table. In these methods, b is a byte, ba is a byte
array, off and len are integers that specify a segment of an array; off represents the
offset (i.e., the starting index of the segment), and len represents the length (i.e., the
number of bytes in the segment); n is a long integer.
Two of the concrete classes for byte stream I/0, which extend the two abstract
classes, are FileinputStream and FileOutputStream, as shown in Figure 8.16.
Instances of these two classes can be constructed by pecifying the filename.
_ .... I
368 • Object-Oriented Application Frameworks
Constructor Description
The primitive I/O capabilities provided in these classes make reading and writing
data of any type possible, but hardly convenient.
PURPOSE
This example demonstrates reading and writing a two-dimensional matrix using the
primitive I/O capabilities provided in the FileinputStream and FileOutput-
Stream classes.
DESCRIPTION
Wri teMatrix1 writes a 2 x 3 matrix to a file. It first writes the number of rows and
column of the matrix, followed by the numbers in the matrix. ReadMatrix1 reads
the file written by Wri teMatrix1 and restores the matrix.
SOLUTION
The data to be written to file are defined in the following interface:
Matrix data
public interface MatrixData {
double [][] data= {
8.4 The Input/Output Framework • 369
In the following Wri teMatrix1 class, the wri teint () method converts an
integer value to a byte array and writes the byte array to the output stream. Similarly,
the wri teDouble O method converts a double value to a byte array and writes the
byte array to the output stream.
'Class WriteMatrix1
import java.io .• ;
public class WriteMatrixl implements MatrixData {
public static void main(String[] args) {
int row= data.length;
int col= data[O] . length;
inti, j;
for (i = O; i < row; i++) {
for (j = O; j < col; j++) {
System . out . println( 11 data[" + i + "] [ 11 + j + "] = II +
data[i] [j]);
}
}
if (args.length > 0) {
try {
FileOutputStream out= new FileOutputStream(args[O]);
write!nt(row, out);
write!nt(col, out);
for (i = O; i < row ; i++) {
for (j = O; j < col; j++) {
writeDouble(data[i] [j], out);
}
}
out.close();
} catch (! □Exception e) {
e.printStackTrace() ;
}
}
}
Running this program creates a file named data! . out and produces the follow-
ing output:
In the following Read.Matrix! class, the readint () method reads 4 bytes from
the input stream into a byte array and converts the byte array to an integer value.
Similarly, the readDouble O method reads 8 bytes from the input stream into a byte
array and converts the byte array to a double value.
a- BeadNatrist
import java.io.•;
public class ReadMatrixl {
static double[][] data;
public static void main(String[] args) {
if (args . length > 0) {
try {
FileinputStream in= new FileinputStream(args(O]) ;
int row = readint(in);
System . out .println( 11 row = 11 + row);
int col= readint(in);
System.out.println( 11 col = 11 + col);
data • new double[row] [col];
for (inti= 0; i < row; i++) {
for (int j • O; j < col ; j++) {
data[i] (j] • readDouble(in);
System.out .println( 11 data[ 11 + i + "] (" + j + "] = 11 +
data(i] [j]);
}
}
.--
8.4 The Input/Output Framework
• 371
} catch (!□Exception e) {
e .printStackTrace () ;
}
}
}
Running this program with the datal. out file produced by Wri teMatrix1 as
the input produces the following output:
Figure 8.17
lnputStream Datalnput
,5
The data input I
I
stream and the I
I
I
data output I
FilelnputStream FilterlnputStream I
stream. I
I
I
I
I
I
I
I
OatalnputStream -----------'
OutputStream DataOutput
,5
I
I
I
I
I
I
FileOutputStream FilterOutputStream I
I
I
I
I
I
I
I
I
DataOutputStream -------- - - '
8.4 The Input/Output Framework • 3 73
Constructor Description
DatainputStream(in) Creates a data input stream that reads data from the
specified input stream in
Data0utputStream(out) Creates a data output stream that writes data to the
specified output stream out
PURPOSE
This example demonstrates reading and writing a two-dimensional matrix using the
1/0 decorators DatainputStream and Data□utputStream.
DESCRIPTION
The Wri teMatrix2 and ReadMatrix2 classes perform the same tasks as the
Wri teMatrix1 and ReadMatrixl classes in Example 8.14. The main differences
are the construction of the data input and output streams, which are shown in boldface
in the following programs. The wri teint (), wri teDouble O, readint O , and
readDouble () methods in the previous examples are no longer needed.
SOLUTION
Class WriteMatrix2
import java .io.• ;
public class WriteMatrix2 implements MatrixData {
public static void main(String[) args) {
int row= data.length;
int col= data[0) .length;
int i, j;
for (i = 0; i < row; i++) {
for (j = 0; j < col; j++) {
System.out .pri ntln ( "data [ II + 1• + ") [ " + J. + 11) : II +
data[i) [j));
}
}
if (args.length > 0) {
try {
DataOutputStream out• .
new OataOutputStream(new FileOutputStream(args[O])),
374 ■
Object-Oriented Application Frameworks
out.writelnt (row);
out . writelnt (col) ;
for ( i = O; i < r ow; i++ ) {
for (j = O; j < col ; j++) {
out.writeDouble (data[i] [j] );
}
}
out.close();
} catch (!□Exception e) {
e.printStackTrace();
}
}
}
}
Chm ReadMatrix2
import java.io.*;
public class ReadMatrix2 {
static double[] [] data;
public static void main(String[J args) {
if (args.length > 0) {
try {
DatainputStream in=
nev DatainputStream(new FileinputStream(args[O]));
int row= in.readlnt();
System.out . println( 11 row = 11 + row);
int col= in.readint();
System.out.println( 11 col = 11 + col);
data= new double[row] [col];
for (inti= O; i < row; i++) {
for (int j = O; j < col; j++) {
data[i] [j] = in.readDouble();
System.out.println( 11 data[" + i + "] [ 11 + j + 11 ] = 11 +
data[i] [j]);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Constructor Description
Figure 8.18
InputStream Datalnput
The buffered input ~
I
I
stream and the I
I
buffered output I
BufferedlnputStream DatalnputStream
FileOutputStream FilterOutputStream
DataOutputStream
BufferedOutputStream
376 • Object-Oriented Application Frameworks
PURPOSE
This example demonstrates the use of buffered input and output streams.
DESCRIPTION
The Wri teMatrix3 and ReadMatrix3 classes perform the same tasks as in Exam-
ples 8.14 and 8.15. The main differences are the construction of the buffered input
and output streams, which are shown in boldface in the following programs.
SOLUTION
Caa Vrit•Matrix3
import java.io . •;
public class WriteMatrix3 implements MatrixData {
public static void main(String[] args) {
int row= data .length;
int col= data[O] . length;
inti, j;
for (i = O; i < row; i++) {
for (j = O; j < col; j++) {
System.out.println("data[" + i + "] [" + j + "] = 11 +
data[i] [j]);
}
}
if (args.length > 0) {
try {
DataOutputStream out~
nev DataOutputStream(
nev BufferedOutputStream(
nev FileOutputStream(args[O])));
out.vritelnt(row);
out . writelnt(col);
for (i = O; i < row; i++) {
for (j = O; j < col; j++) {
out.writeDouble(data[i] [j]);
}
}
out .close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
-
8.4 The Input/Output Framework ■ 377
Class ReadMatrix3
import java.io.•;
public class ReadMatrix3 {
static double[)[) data ·
'
public static void main(String[) args) {
if (args.length > 0) {
try {
DatainputStream in.
nev DatainputStream(
nev BufferedinputStream(
. nev FileinputStream(args[O))));
int row= in . readint() ;
System . out .println ( "row ="+row);
int col= in.readint();
System . out .println (" col = " +col) ;
data= new double[row) [col];
for (inti= O; i < row; i++) {
for (int j = O; j < col; j++) {
data[i] [j] = in . readDouble ();
System . out .println ("data[" + i + " ] [" + j + "] = " +
data[i] [j]);
}
}
} catch (!□Exception e) {
e.printStackTrace();
}
}
}
}
3. All the object that are directly or indirectly referenced by a given object o along with the reference
relation among the e objects fonn what is known as the object graph of o.
. ..
1";'
,..,,,., I I
378 ■
Object-Oriented Application Frameworks
Figure 8.19
InputStream Data Input
LS
The object input
stream and the ob-
ject output stream.
FilelnputStream ObjectlnputStream ------~ Objectlnput
of the classes in j ava. lang and the collection classes. Arrays can also be serialized
as long as the elements are serializable.
The methods for serializing and deserializing are declared in the Obj ectinput
and Obj ectOutput interfaces, which extend Datainput and DataOutput, respec-
tively. The input method is readOb j ect () ; the output method is wri teOb j ect ().
The Obj ectinputStream and Obj ectOutputStream are two UO filter classes
that implement the Objectinput and ObjectOutput interfaces, respectively, as
shown in Figure 8.19. The constructors are described in the following table:
Constructor Description
Obj ectinputStream (in) Creates an object input stream that reads data from
the specified input stream in
Obj ectOutputStream (out) Creates an object output stream that writes data to
the specified output stream out
PURPOSE
This example demonstrates reading and writing a two-dimensional matrix using
object serialization.
DESCRIPTION
The Wri teMatrix3 and Read.Matrix3 classes perform the same tasks as in Exam-
ples 8.1 4-8. 16.
8.4 The Input/Output Framework • 379
SOLUTION
Using object serialization, we can easily read and write the entire array with a single
method invocation.
Class WriteHatrix4
import java . io .•;
Claa ReadMatrix4
import java.io .*;
public class ReadMatrix4 {
static double[][] data ;
public static void main(StringO args) {
if (args . length > 0) {
try {
0bjectinputStream in• [OJ)) ·
new 0bjectinputStream(nev FilelnputStream(args '
data. (double(](]) in.read0bject();
int row = data .length;
int col= data [0] . length;
for (int i= 0; i < row ; i++){
for (l· nt J. = 0; J. < col; j ++){ 11 +
System .out .println(11data(11 + i + "] (11 + j + 11) =
data (i](j ] );
}
}
380 ■
--
Object-Oriented Application Frameworks
} catch (Exception e ) {
e . printStackTrace() ;
}
}
}
}
The design of the Java input/output framework illustrates the Decorator design pattern.
The basic byte-based 1/0 capability is provided by the InputStream and Output-
Stream classes. Also, there are many add-on features that enhance the basic 1/0
capability, such as reading and writing data of primitive types, read and write objects,
buffered read and write, compression and decompression of data, encryption and de-
cryption of data, and so on. These add-on features can be combined in many different
ways, and in different contexts we may need to use different combinations of them.
One approach to satisfying all the potential needs is to extend the InputStream and
0utputStream classes to add these features. A large number of classes are necessary
to cover every possible combination of these add-on features , such as
■ BufferedDatainputStream,
■ EncryptedDatainputStream,
■ CompressedinputStream,
■ EncryptedCompressedDatainputStream,
■ and so on.
An alternative approach is to define each add-on feature as a separate class known as
a decorator, or filrer, 4 that adds a feature to the basic I/O capability through object
composition. A particular combination of the add-on features is composed by adding
the corresponding decorators of these add-on features to the basic I/O capability, as
m
new Decorator2(new Decorator} (inputStream))
This approach avoids an explosion of the number of classes and provides flexibility
through object composition.
4. Decorator seems 10 be a belier name than filter because decorate connotes addi ng, whereas filrer
connotes removing.
8.4 The: Input/Output Framework ■ 381
Component "
operation()
I
t I o Component
ConcreteComponent Decorator
operation() operation() 0 · · · ----\ component .operation()
I
t I
ConcreteDecoratorA ConcreteDecoratorB
-
operation() operation() 0
added State addedOperation() \
'\
super.operation()
addedOperation()
Component (e.g., I nput St re am), which define the interface for objects that
can have re ponsibilitie added to them dynamically.
Co11creteCompone11t (e.g., FileinputStream), which define an object to
which additional respon ibilitie can be added.
Decorator (e.g., FilterinputStream), which maintain a reference to a
component object and defines an interface that confonns to the component's
interface.
ConcreteDecorator (e.g., DatainputStream and Buff eredOutputStream),
which adds responsibilities to the component.
For a detailed di cussion of the Decorator design pattern, see Gamma et al.
[ 1995].
382 • Object-Oriented Application Frameworks
In Java programs, strings are repre ented internally in Unicode, in which all char-
acters are encoded in two bytes. Externally, text fil es are stored in various character
encodings supported by the platfonn on which the programs are running . The default
character encoding of a machine is often detennined by the machine's locale . A locale
is a geographic region that share the same language and customs. For example, in
the United States, the default character encoding is ISO-8859-1, commonly known as
the ASCII code, in which all characters are encoded in a single byte. ln the People's
Republic of China, the default character encoding is GB-2312, in which all Latin
characters are encoded in a single byte, and all Chinese ideographs are encoded in
two bytes.
The character-based 1/0 streams perfonn conversions between the Unicode and
the locale-sensitive character encodings when reading and writing strings. In other
words, character streams are locale sensitive, but byte streams are not.
Two abstract classes, Reader and Writer, are the roots of inheritance hierar-
chies of the character-based input and output streams, respectively. The relationships
among the key character 1/0 streams are shown in Figure 8.20. The Reader and
Writer classes provide the basic capabilities for reading and writing characters. Their
methods are summarized in the following table. The parameter c represents a char-
acter, ca represents a character array, and off and len are integers that specify a
Figure 8.20
Reader
The character-
based readers and
writers.
InputStream lnputStream Reader Buffered Reader
File Reader
Writer
t
OutputStream OutputStream Reader BufferedWriter
segment of an array; off represents the offset (i.e., the starting index of the segment),
and len represents the length (i.e., the number of elements in the segment).
Constructor Description
The FileReader and FileWri ter are two concrete character streams that
provide the basic character-ha ed VO capabilities. The constructors of these two
clas es are summarized in the following table:
Constructor Description
The BufferedReader and Buff eredWri ter are two filters that support buff-
ered character-based 1/0. The constructors of these two classes are summarized in the
following table:
Constructor Description
The Buff eredReader class adds the method readLine (), which reads a line
of text.
PrintWri ter is a useful filter class that supports writing values of various data
types by converting them to their string representations. The PrintWri ter class has
the following constructor:
PrintWriter(writer)
8.4 The Input/Output Framework • 385
which creates a print writer that writes data to the specified writer. The PrintWriter
class provides the following two methods, which are overloaded on all data types; i.e.,
the parameter v can be of any type.
Method Description
PURPOSE
This example demonstrates reading and writing a two-dimensional matrix using
character-based 1/0 streams.
DESCRIPTION
To read and write data using strings, all data must be converted to and from their string
representations. To write data, we must insert delimiters between consecutive values.
SOLUTION
The Wri teMatrix5 class writes the two-dimensional array to a text file. Each line
contains a single value. The first two lines contain the number of rows and columns
followed by the values in the matrix.
Class WriteMe.trix5
import java . io.•;
public class WriteMatrixS implements MatrixData {
public static void main(String(] args) {
int row = data .length;
int col= data(O] .length ;
inti, j;
for (i = O; i < row; i++) {
for (j = O; j < col ; j++) {
System . out .println ( ll data (11 + 1. + 11) [" + j + 11) : II +
data[i] [j]);
}
}
C
386 ■
..
Object-Oriented Application Frameworks
The ReadMatrix5 class reads the text file written by Wri teMatrix5 and re-
stores the array.
Class leadNatrix5
import java.io.•;
public class ReadMatrix5 {
stati c double[] 0 data;
public static void main(String(] args) {
if (args.length > 0) {
try {
BufferedReader in=
new BufferedReader(
new FileReader(args[O])) ;
String line;
line= in.read.Line();
int row= Integer.parselnt(line) ;
System . out .println( 11 row = 11 + row);
line= in.readLine();
int col= Integer . parselnt (line) ;
System.out . println( 11 col = 11 + col);
data z new double[row] [col];
for (inti= O; i < row; i++){
for (int j = O; j < col; j++){
line= i n .read.Line ();
data[ i ] (j] = Double .valueOf(line) . doubleValue();
System.out.println("data[ 11 + i + 11 ] ( 11 + j + 11 ] = 11 +
data(i] (j]);
}
}
8.4 The Input/Output Framework • 387
} catch (!□Exception e) {
e . printStackTrace() ;
}
}
}
}
Character streams are also able to use character encodings that are different from
the default character encoding.
PURPOSE
This example demonstrates the use of character-based readers to view text files in any
character encoding.
DESCRIPTION
This GUI application makes use of Swing components. It consists of an instance of
JTextArea inside an instance of JScrollPane, which allows the text area to be
scrolled both vertically and horizontally. The program expects two arguments: the
text file name and the character encoding.
SOLUTION
Class UniveraalTextViewer
import java.avt.*;
import java.avt.event .*;
import java.io.*;
import javax.swing .*;
public class UniversalTextViewer extends JPanel {
public UniversalTextViewer(String filename, String enc) {
setLayout(new BorderLayout ()) ;
JTextArea textArea = new JTextArea (40, 80);
textArea . setEditable (false );
textArea . setFont (new Font ( "Monospaced", Font .BOLD, 16)) ;
add(new JScrollPane(textArea) , BorderLayout . CENTER) ;
try {
BufferedReader in=
new BufferedReader (
new InputStreamReader (new Filel nputStream(filename) ,
enc));
String l i ne ;
while ((line = in.readLi ne ()) ! = null ) {
textArea . append(line + 11 \n");
}
} catch (IOE.xcept i on e) {
e . printStackTrace ( ) ;
}
}
d
Figure 8.21 shows a screen shot of the universal text viewer presenting a text file
in simplilied Chinese (GB2312 encoding).5
5. For the Chinese cbaracLers Lo be displayed properly, Chinese fonLS must be installed and the
font . properties file of the JDK or JRE musl be configured to use the appropriate fonts . See the
on line supplemeol of the book for details of setting up the fonts.
r, - - - --. -- - - - - -- - -
Figure 8.21
!iS14if@iiiit!titi\Mhiili11t·l•i·IWM=fMtl--- -1 DIX I
The universal text
I •
viewer. I
I fi
~-IQJ
•±•~~~~~ "~~~~~~
8.4 The Input/Output Framework ■ 389
Random access files support reading and writing data at any position in the files
and can support reading and writing at the same time. The RandomAccessFile
cla s provides the basic capabilities of random-access 1/0. The constructor of the
RandomAccessFile class is the following:
Constructor Description
Method Description
seek (l ) Moves the read/write po ition to the 1-th byte counting from the
beginning of the file
ski pBytes ( i) Move the read/write po ition i bytes relative to the current
po ition; move forward if i > 0 and move backward if i < 0
PURPOSE
Thi example demon trate the u e of a random-acce file.
DESCRIPTION
The RandomAc cessFile cla doe not implement the Obj ectinput and the Ob-
j ect0utput interface . So erialized object cannot be directly written to rando_m -
acce file . In thi example, we extend the RandomAccessFile to upport reading
and writing erialized object in random-acce file .
390 • Object-Oriented Application Frameworks
SOLUTION
Because serialized objects vary in size, for each object, we first store its size as
an integer (4-byte) and then store the serialized object itself. The wri t eObj ect 0
method first serializes the object in a byte array and copies the array to the fi le, and then
the total number of bytes written to the file (including the size count) is returned . The
readOb j ect O method reads a serialized object at the current read/write positio n.
It first reads the size count and then reads the serialized object into a byte array and
deserializes the object.
The output is
venus¾ java TestWrite obj.out
Tic written at offset O size= 14
Tac written at offset 14 size= 14
Toe written at offset 28 size= 14
The following is a simple test program that read the random-acce s file created
by TestWrite. It expects the first argument to be the file name and the following
arguments to be integers specifying the offset at which the object are to be read.
obj= i n.roadObjec t ( );
System. out . println (obj +"read at off set " + offset) ;
}
} catch (Exception e) {
e . printStackTrace() ;
}
}
}
}
The output is
"CHAPTER SUMMARY
• Serialization is the proce of writing an object and all the objects that are directly
or indirectly referenced by the object to a stream. Deserialization is the process
of restoring an object and all the objects that are directly or indirectly referenced
by the object that have been crialized.
• The Composite design pattern compose objects into tree tructures to represent a
part-whole hierarchy. The Compo ite design pattern let client treat individual ,
objects and compositions of object uniformly. The Compo ite design pattern i
should be used to repre ent a part-whole hierarchy of objects or to allow clients I
to treat aJI objects in the compo ite structure uniformly.
■ The Decorator design pattern attaches additional responsibilities to an object dy-
namically. Decorators provide a flexible alternative to subcla sing for extending
functionality. The Decorator design pattern should be used to add responsibili-
ties to individual objects dynamically without affecting other objects in the same
class, or to avoid an explosion of subclasses caused by an extension by subclas -
ing.
■ The common aspects of related classes should be handled unifonnly. These com-
mon aspects are usually interfaces or abstract classes. The greater the uniformity,
the simpler and more useful is the design.
FURTHER READING
Walrath, K., and M. Campione (1999). The JFC Swing Tutorial: A Guide to Con-
structing GU/s. Addison-Wesley.
l EXERCISES
8.1 The Set interface defines only the most basic Define and implement a class that supports
operations on sets. Some of the common these operations on sets as algorithms that
operations on sets that are not included in can be applied to any concrete classes that
the Set interface and their definitions are the implement the Set interface.
following :
8.2 Design and implement a class Car that de-
• The union of two sets: scribes the key characteri. tics of a car, uch a
model, model year, manufacturer, color, hor e-
power, number of cylinders, and o on. The
Car class should implement the Comparable
• The intersection of two sets:
interface. The natural order of the car objects i
based on the ascending orders of the following
attributes of the cars in descending significance:
• The nonsymmcLric difference of two sets: • The manufacturer
• The model
• The model year
Projects • 39 5
CHAPTER OVERVIEW
In this chapter, we present a case study in developing g@phical user interfaces (GUls),
using the Java Foundation Class (JFC). We also demonst@te the ite@tive development
process by designing and implementing a graphical d@wing pad in successive incre-
ments. We introduce two new design patterns: Factory Method and State.
9.1 PLANNING
In this chapter, we develop a complete GUI application u ing the lightweight compo-
nent in the Swing package. The GUI application to be developed i a drawing tool
that supports the following:
nd
The aim of this case study is to illustrate the iterative development proces~ a ~ e
use of design patterns. The use of design patterns play a crucial role in the tt~rauve
development process because each increment must be designed to be extensible to
accommodate design changes and enhancements in subsequent iterations.
The plan is to divide the development process into six iterations:
Iteration I : Creating a simple scribble pad, which consists of only a canvas for
scribbling.
Iteration 2: Adding support for saving and loading drawings, a menu bar, various
dialogs.
Iteration 3: Refactoring to support various tools for drawing various shapes .
Iteration 4: Adding tools for drawing lines, rectangles, and ovals.
Iteratio11 5: Refactoring and adding tools for drawing filled rectang les and ovals.
Iteration 6: Adding a tool for typing text.
In order for students to study the changes between iterations, the code produced in
each iteration is placed in a separate package.
The goal of the first iteration is to deliver an initial version of the application th
. . . b f" In
~~n_tams a _nurumum su s~t o 1eatures. order to keep it as simple as possible, the
at
1rut1aJ vers10n of the drawmg
. tool only supports scribbling · The applicat1·00 consists
·
of a frame that contams the canvas for scribbling only. A screen shot of th · · •
version . 1e sen"bbl e pad 1s
. of t be s1mp . shown in Figure 9 .1. e m1tial
t
p
9.2 ltffation 1: A Simple Scn'bble Pad ■ 399
MouseListener MouseMotionListener
' I
' I
' I
' I
' I
ScrlbbleCanvasUstener
The initial design of the simple scribble pad is shown in Figure 9.2. The UML
diagrams in this chapter contain shaded classes to indicate that these clas es are the
ones to be developed in the current iteration, and the rest are classes and interface
either in the Java 2 Class Library or developed in previous iterations. The classes
to be developed in the first iteration are in the package scribble1, and they are
summarized in the following table:
Class Description
Fields Description
C~ acribble1. ScribbleCanvaa
-
package scribble! ;
import java .awt .* ;
import java.awt . event . *;
import java .util.EventListener;
import j avax . swing . *;
public class ScribbleCanvas extends JPanel {
public ScribbleCanvas () {
listener= new ScribbleCanvasListener(this);
add.MouseListener ((MouseListener ) listener) ;
add.MouseMotionListener((MouseMoti onListener) listener);
}
There are two event listener interfaces that deal with mouse events:
1. j ava . awt. event. MouseListener, for events related to the action on the
buttons of the mouse, such as button press and release.
2. j ava. awt. event. MouseMotionListener, for events related to the move-
ment of the mouse, such as mouse move or drag.
Methods Description
previous mouse position to the current position and updates the current mouse position
in ScribbleCanvas.
The mouseReleased () method handles the end of a drawi ng troke. It sets the
boolean flag mouseButtonDown of the canvas to false.
The constructor of ScribbleCanvas class [p. 400] creates an instance of
ScribbleCanvasListener and registers the listener as both MouseListener and
MouseMotionListener of the canvas.
The Scribble class is the main application class of the initial version of the scribble
pad. It extends the j avax. swing. JFrame class. A frame is created in the main ()
method, and an instance of the Seri bbleCanvas is placed at the center of the frame.
}
9.3 Iteration 2: Menus, Options, and Files ■ 403
This completes the first iteration of the drawing pad. The result is a simple but
completely functional GUI application.
9.3.1 Strokes
One of the deficiencies of the initial version is that the drawings are directly painted
onto the canvas by the ScribbleCanvasListener object, and they are not tored
internally. As a result, if the application frame is moved, covered, or minimized.
the drawings disappear. To retain the drawings after the application frame i moved,
Ale Option
The scribble pad-
iteration 2.
-
404 ■ Design Case Study: A Drawing Pad
'' \ /
'' .. \
\ I
I
ScribbleCanvasUstener
1
*
Point
covered, or minimized, the drawings must be stored internally and repainted whenever
necessary.
The drawings are stored in the following structure:
■ Each drawing consists of a list of strokes.
■ Each stroke consists of a list of points and the color of the stroke.
The scribble2. Stroke class represents a single stroke in a drawing. Since we
want to be able to save the drawings in files, we make Stroke class serializable so
that we can use the serialization mechanism to save and load drawings. The fields of
the Stroke class are as follows:
Fields Description
Methods Description
Classscribble2 . Stroke
package scribble2;
import j ava . ut i l.• ;
import java . io.Seri alizable;
i mport j ava . awt . Point ;
i mport j ava . awt . Color ;
public class Stroke implements Seriali zable {
public Stroke() {}
public Stroke (Color color) {
this . color= color ;
}
public void setColor (Color color) {
this . color= color;
}
public Color getColor () {
return color ;
}
public void addPoint (Point p) {
if (p != null) {
points . add (p) ;
}
}
public List getPoints () {
return points ;
}
protected List points= new ArrayList ( ) ;
protected Color color= Color . black ;
}
Fields Description
Methods Description
Class1cribble2.ScribbleCanvas
package scribble2;
import java.awt.Color ;
import java .awt .Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java .util.•;
import java. io.•;
import java.awt . event . •;
import java .util.EventListener;
import javax.swing.•;
public class ScribbleCanvas extends JPanel {
public ScribbleCanvas() {
listener= new ScribbleCanvasListener(this);
addMouseListener((MouseListener) listener);
addMouseMotionListener((MouseMotionListener) listener);
}
9 .3 Iteration 2: Menus, Options, and Files • 407
e the listeners of the menu items in the menu bar. The methods
nested cIasses, Which ar . . .
of the scribble2. Scribble class are summarized m the followmg table:
Methods Description
The nested classes of the scribble2. Scribble class are summarized in the
foIJowing table:
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
if (exitAction ! • null) {
exitAction . actionPerformed(new ActionEvent
(Scribble.this, 0, null));
}
}
});
}
In the constructor of the Scribble class, the menu bar is created and placed
above the canvas. The menu bar is created by the following createMenuBar 0
method:
Note that each menu item has an associated action listener to handle the action of
the menu item. The action listeners are all defined as nested classes of the Scribble
class.
The simplest action listener is the AboutListener, which handles the action
of the About menu item in the Help menu. It displays a simple dialog box using the
javax. swing . JOptionPane class as shown in Figure 9.5. The JOptionPane cla
provides various static methods to display the most commonly used dialog boxes with
a single method invocation.1
I. See API documentations on Lhe j avax . swing . JOpt ionPane class for details.
9 .3 Iteration 2: Menus, Options, and Files • 413
Figure 9 .5
D
The NewFileListener class handles the action of the New menu item in the
File menu. The action is delegated to the newFile () method, as follows:
The OpenFileLi stener class handle the action of the Open menu item in the
File menu. It uses the j a v ax. swi ng . J Fi leCh ooser clas to di play a file dialog
as shown in Figure 9.6. After a fi le nan1e is elected using the fi le dialog, the action
is delegated to the openFi le () method with the elected file name, as follow
String filename=
chooser.getSelectedFile ().getAbsolutePath();
openFile(filename) ;
}
}
}
}
The SaveFileListener class handles the action of the Save menu item in the
File menu. The action is delegated to the saveFile () method, as follows :
of1cribble2.Scribbledass:SaveF1leLiatener
class SaveFileListener implements ActionListener {
The SaveAsFileListener class handles the action of the Save As menu item
in the File menu. It also uses the j avax. swing. JFileChooser class to display a
tile dialog similar to the one shown in Figure 9.6. After a file name is selected using
Figure 9.6
the file dialog, the action is delegated to the saveFileAs () method with the selected
file name, as follows :
The Exi tListener class handles the action of the E.xit menu item in the File
menu. It displays a simple dialog box using the j avax . s-wing. JOptionPane class
as shown in Figure 9.7 to prompt the user to save the current drawing.
Figure 9.7
The methods dealing with files maintain the current file name and delegate most
of the work to the corresponding method in the scribble2. ScribbleCanvas class
[p. 406].
The nested ColorListener class handles the action of the Color menu item in the
Option menu. It displays a custom dialog box as shown in Figure 9.8. The custom
color dialog is defined in the scribble2. ColorDialog class.
Agure 9.8
package scribble2 ;
import java . avt.•;
import java. avt.event . •;
import javax. swing . • ;
public class ColorDialog extends JDialog implements
ActionListener {
public ColorDialog(JFrame owner, String title) {
this(owner, title , Color.black) ;
}
bottom.Panel . add(cancelButton);
getContentPane().add(bottomPanel, BorderLayout.SOUTH);
pack();
}
if (cellHeight < 5) {
cellHeight = 5;
}
i f (xpad < 2) {
xpad • 2;
}
if (ypad < 2) {
ypad a 2;
}
this.cellWidth = cellWidth ;
this . cellHeight • cellHeight ;
this.xpad • xpad ;
this . ypad = ypad;
rowCount = colorGrid.length ;
columnCount • colorGrid[O].length;
dimension= new Dimension((cellWidth + xpad) •
columnCount + xpad,
(cellHeight + ypad) •
(rowCount + 1) + ypad);
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent event) {
Point p = event . getPoint();
inti= (p .y / (ColorPanel.this.cellHeight +
ColorPanel . this . ypad));
int j = (p.x / (ColorPanel . this.cellWidth +
ColorPanel . this.xpad));
if (i < rowCount tt
j < columnCount) {
color= colorGrid[i][j] ;
repaint();
}
}
});
}
It uses the j avax. swing. ColorChooser class to display a color chooser dialog
as shown in Figure 9.9.
This completes the second iteration of the drawing pad .
9..4 ttaation 3: Refactoring • 421
Figure 9.9
Preview
~ -1 C~cel 11 Beset I
The ultimate goal of the drawing pad i to upport the drawing of variou type of
shapes, such a lines, ovals, and rectangles. The current version of the drawing pad
supports only scribbling. The design of the current version of the drawing pad is
also rather simpleminded to support scribbling only. ln order to upport the drawing
of variou types of shape , the de ign of the drawing pad mu t be improved to
accommodate such extension . The aim of thi iteration i to refactor the design
to improve its exten ibility. The functionality of the re ulting application is identical
to that of the previou iteration.
Refactoring the
scribble pad-the •
shapes. ScrlbbleCanvas
Point
of shapes, we introduce an abstract class Shape to represent all shapes that can be
drawn, with stroke being one of such shapes. The refactored design concerning the
shapes is shown in Figure 9.10. Other shapes include lines, ovals, and rectangles. The
subclasses representing these other shapes are introduced in the next iteration (see
Figure 9.13).
The reason for Shape being an abstract class instead of an interface is that there
are some features, that is, fields and methods, that are common to all shapes, for ex-
ample, the color of each shape. It is more appropriate to define these common features
in a common superclass. The field color and the associated methods previously de-
fined in the Stroke class are moved to the superclass Shape. The abstract method
draw() is intended for each subclass to define how each shape should be drawn.
Abltnctdmscribble3.Sh
package scribble3;
import java.awt.•;
import java.io.Serializable;
public abstract class Shape implements Serializable {
public Shape(){}
public Shape(Color color) {
this . color= color;
}
~---
9.4 Iteration 3: Refactoring • 423
Cla,s scribble8.
package scribble3 ;
import java.util . •;
import java.awt.Point;
import java.awt .Color;
import java.awt.Graphics;
public class Stroke extends Shape {
public Stroke(){}
public Stroke(Color color) {
super(color);
}
In order to draw different shapes, such as lines, ovals, and rectangles, the behavior
of the canvas listener must be different when drawing different shapes. One possible
approach is outlined in the following code fragments. First, we define a set of constants
to represent possible shapes that need to be drawn and use a field current Shape to
indicate the currently selected shape:
public class DrawingPad {
II constants representing shapes
public static final int SCRIBBLE= O;
public static final int LINE= 1;
public static final int RECTANGLE= 2;
public static final int OVAL= 3;
// the currently selected shape
protected int currentShape = SCRIBBLE;
public void setCurrentShape(int shape) {
currentShape = shape;
}
public int getCurrentShape() {
return currentShape;
}
II other fields and methods
}
• I
111, • •• .
9 .4 Iteration 3: Refactoring • 425
Figure 9.11
I ScrlbbleCanvasUstener p--~71__'ool___
Rdactoring the
scribble pad-the
*
tools. AbstraotTool
ScribbleTool
426 ■ Design Case Study: A Drawing Pad
The AbstractTool class implements the Tool interface and provides default
implementation to features that are shared by all tools.
iKribbl.S.OnractTool
package scribble3;
The ScribbleTool class is a concrete tool that defines the behavior for scrib-
bling. Note that the implementation of the ScribbleTool class is identical to por-
tions of the implementation that were previously in the scribble2. Scribble-
CanvasListener class.
package scribble3;
import java.avt.•;
public class ScribbleTool extends AbstractTool {
public ScribbleTool(ScribbleCanvas canvas , String name) {
super(canvas, name);
}
The behavior of drawing each shape is now encapsulated in a tool for that
shape. The scribble3. ScribbleCanvasListener class contains a reference to
the current tool being used for drawing, that is, the scribble tool in this iteration,
since it is the only tool supported. It is to this tool that responses to events of the
scribble3. ScribbleCanvasListener class are delegated.
Clills acribbl.S. S c r l b ~
package scribble3;
import java.awt . • ;
import java.awt.event.•;
public class ScribbleCanvasListener
implements MouseListener, MouseMotionListener {
public ScribbleCanvasListener(ScribbleCanvas canvas) {
this . canvas= canvas;
tool= new ScribbleTool(canvas, "Scribble");
}
The refactoring concerning the tools is also an application of the Strategy design
pattern. In the next iteration, the design will be further extended to support multiple
tools, in which case the Strategy pattemconcerning the tools here will become a part
of a new design pattern-State (see Section 9.5.3).
The various components of the drawing·pad will be enhanced and extended in several
iterations. Enhancements can usually be accomplished in two ways: :
I. Modifying the original class
2. Building a new class that extends the original class
Usually the same enhancement can be accomplished in either way. However, enhance-
I
ment by extending the original class has several advantages:
• It is nondestructive. The original implementation is untouched, and the enhance-
ments or changes can be easily re.versed.
• It is more suited for iterative development, in which systems are built in small
increments. The separate classes fonn the natural boundaries of the increments.
• It is the only option when the source code of the original class is not available
or the original class should not be modified for some reason; e.g., it is used by
other programs that should not be affected by the enhancements or changes to
the class.
In iterations 2 and 3, we modified the original code in classes Seri bble, Scribble-
Canvas, and Seri bbleCanvasListener to implement enhancements and changes.
In all the subsequent iterations, we will use the extension technique to implement ,
enhancements and changes. Specifically, new versions of the canvas and canvas
9.4 Iteration 3: Refactoring • 429
)jstener classes will extend the previous version of the canvas and canvas listener
classes, respective]y.
The scribble3 . Scribble class is nearly identical to the scribble2 . Scrib-
ble class. The only difference is that in scribble3. Scribble the canvas is not
created directly by using the new operator; rather it is created indirectly by using a
factory method-makeCanvas () . Using a factory method instead of the new oper-
ator allows subclasses of the Seri bble class to instantiate instances of subclasses of
ScribbleCanvas, which represent enhanced versions of the canvas. To replace the
canvas with an enhanced canvas, a subclass of the Scribble class can override the
factory methods to create instances of the enhanced canvas. No change in the Serib-
ble class would be necessary. The benefits of using factory methods will become clear
in iteration 4 [p. 445).
package scribble3 ;
import java.awt . •;
import java.awt.event.•;
import java.io.•;
import javax.swing.•;
public class Scribble extends JFrame {
public Scribble() {
super("Scribble Pad");
// calling the factory method
canvas• makeCanvas() ;
getContentPane().setLayout(new BorderLayout());
menuBar = createMenuBar();
getContentPane() . add(menuBar, BorderLayout .N0RTH);
getContentPane().add(canvas, BorderLayout.CENTER);
addWindowListener (new WindowAdapter() {
public void windowClosing(WindowEvent e) {
if (exitAction ! null) {
3
package scribble3;
import java.avt.Color;
import java.avt.Dimension;
import java.avt .Graphics;
import java. avt.Point;
import java.util.•;
import java.io.•;
import java.avt.event . •;
import java.util . EventListener;
import javax.swing. •;
public class ScribbleCanvas extends JPanel {
public ScribbleCanvas() {
II calling the factory method
listener= makeCanvasListener();
addMouseListener((MouseListener) listener);
addMouseMotionListener((MouseMotionListener) listener);
}
...... se--z
9.4 Iteration 3: Refactoring • 431
This completes the third iteration of the drawing pad, which is concerned with
refactoring only to improve extensibility without enhancement in functionality.
In this iteration, we enhance the functionality of the drawing pad. The refactoring
of the design in the last iteration allows these enhancements to be added easily and
incremental]y. A screen shot of this version of the drawing pad is shown in Figure 9.12.
The drawing pad has a tool bar at the left, with each tool button representing a
different tool. Clicking on the tool button selects the tool for the drawing. The tools
supported by this drawing pad are the scribbling tool, the line-drawing tool, the
rectangle-drawing tool, and the oval-drawing tool. This version also adds a new
menu-Tools-on the menu bar. A drawing tool can also be selected from the menu
item corresponding to the tool.
Figure 9.12
flle Tools Opdan Help
The drawing pad-
Iteration 4.
Satbllle
une I
j_ ( ) /7 /
JQJa is vers0:wv ,
Remn1111
________
-....,;;;;;;.;;;.;,., ,,
..,
Iteration 4: Adding Shapes and Tools • 433
■ Use of the State design pattern to support the different behaviors associated with
different tools and to switch among different tools dynamically.
■ Use of the Factory Method design pattern to allow flexibility in creating instances
of different subclasses.
In this iteration, the drawing of the following types of shapes will be supported: line,
oval, and rectangle. These three types of shapes share a common characteristic-
each of these shapes can be completely defined by two points: the two end points for
lines and the two diagonally opposite comers for rectangles and ovals. We call these
two points the end points. An abstract class named TwoEndsShape is introduced to
capture this common feature. The design concerning the shapes in this iteration is
shown in Figure 9.13.
The methods of the TwoEndsShape class are summarized in the following table:
Methods Description
OrawlngCanvas Stroke
public void setEnds(int xl, int yl, int x2, int y2) {
this .xl = xl;
this.yl = yl;
this.x2 = x2;
this.y2 = y2;
}
The three types of concrete shapes are defined as concrete subclasses of the
TwoEndsShape class.
drav1. Oval.SM~
package drawl;
import java.avt .• ;
public class DvalShape extends TwoEndsShape {
public void draw(Graphics g) {
int x = Math.min(xl, x2);
int y = Math .min(yl, y2);
int w = Math . abs(x1 - x2) + 1;
int h = Math .abs(yl - y2) + 1;
if (color != null) {
g.setColor(color) ;
}
g . draw□val(x, y, w, h);
}
package drawl;
import java.awt.•;
public class RectangleShape extends TwoEndsShape {
public void draw(Graphics g) {
int x • Math.min(x1, x2);
int y = Math.min(y1, y2);
int w = Math.abs(x1 - x2) + 1;
int h = Math.abs(y1 - y2) + 1;
if (color!= null) {
g. setColor(color);
}
g.drawRect(x, y, w, h);
}
The design concerning the tools in this iteration is shown in Figure 9.14. The drawing
pad provides a set of different drawing tools. Each tool extends the abstract class
AbstractTool in the previous iteration. The Toolkit class represents a set of tools
supported by the drawing pad and keeps track of the currently selected tool. The
responses to a mouse button press or release and mouse dragging on the drawing
Figure 9.14
Scribble
The design of the
drawing pad-the
tools. 1 •
DrawfngPad ic> - -- i Toolkit Tool
AbstractTool
TwoEndsTool ScribbleTool
....,.
9.5 Iteration 4: Adding Shapes and Tools ■ 437
canvas depend on which tool is currently selected. In the Toolkit class, each tool
can be identified either by its name or by its position in the toolkit. In the DrawingPad
class, an integer field is used to indicate the position of the tool to be selected. The
methods of the Toolkit class are summarized in the following table:
Methods Description
a.
package dra'Wl;
import java .util . *;
import scribble3.Tool;
public class ToolKit {
public ToolKit() {}
public int addTool(Tool tool) {
if (tool != null) {
tools . add(tool);
return (tools . size() - 1) ;
}
return -1 ;
}
I
protected Li st tools = new ArrayList(16);
protected Tool selectedTool = null;
}
The refactoring concerning the toolkit illustrates the State design pattern, which
allows encapsulation of the behavior of each tool in a separate class and decouples
the tools from the DrawingPad class.
I
Design Pattern State
Catego,y: Behavioral design pattern.
Intent: Allow an object to alter its behavior when its internal state changes.
Also Known As: Objects for states.
J
9.5 Iteration 4: Adding Shapes and Tools ■ 439
The structure of the State design pattern is shown in the following diagram.
Context State
,~ State
request()Q Iv handle()
t;,
I
,. - - - - - - - _ ,_ - - - - - - - '"I
I I
state.handle()
ConcreteStateA ConcreteStateB
handle() handle()
• When the mouse button is pressed, the current mouse position defines the first
end point of the selected shape.
• When the mouse is dragged, temporary frames of the selected shape are drawn .
These temporary frames follow the mouse as it is being dragged. This process
is commonly known as rubber banding because dragging the mouse is like
stretching a rubber band.
• When the mouse button is released, the current mouse position defines the second
end point of the selected shape. The selected shape defined by the two end points
is drawn.
440 • Design Case Study: A Drawing Pad
Field Description
I ,
442 • Design Case Study: A Drawing Pad
// helper methods
public static void drawLine(Graphics g,
int x1, int y1, int x2, int y2) {
g.drawLine(x1, y1, x2, y2);
}
The mousePressed O method records the coordinates of the first end point and
prepares for rubber banding. Rubber banding is accomplished by using the exclusive-
or (XOR) mode of the graphics context. When the XOR mode is used in a graphics
context and a figure is drawn once, the figure is visible. However, the color of the
figure depends not only on the current pen color, but also on the colors of the pixels
that the figure covers. When the same figure is drawn twice in the same position, the
figure disappears. The mouseDragged() method performs the rubber banding. The
values canvas . x and canvas. y are the coordinates of the intermediate preceding
location of the mouse. The mouseReleased () method records the second end point
of the shape. It sets the mode of the graphics context to the default paint mode. A new
instance of the selected shape is created, and the selected shape is drawn on the canvas.
In this iteration, the main application, the canvas, and the canvas listener classes a11
extend their counterparts in the previous iteration. Factory methods are used in the
ex tended classes to create instances of the extended classes. The design is shown in
Figure 9.15.
______..._..__.
9.5 Iteration 4: Adding Shapes and Tools • 443
Figure 9.15
Scribble ,-1. DrawingPad
The overall design
scribble() Q makeCanvas()
of the drawing 0
pad-iteration 4. I
'\
...
makeCanvas()
...
ScribbleCanvas (} DrawlngCanvas
ScribbleCanvas () 9 makeCanvaslistener()
I '\
...
makeCanvaslistener()
...
ScribbleCanvaslistener f"
1.... I
I DrawlngCanvasllstener I
}
444 • Design Case Study: A Drawing Pad
I
public DrawingCanvas() {
}
I
public void setTool (Tool tool) {
drawingCanvasListener . setTool(tool);
}
The drawl . DrawingPad class is the main application class. It manages the
toolkit and is responsible for creating the tool bar on the left and the tool menu on
the menu bar for selecting the current drawing tool. The methods of the DrawingPad
class are summarized in the following table:
Methods Description
The structure of the Factory Method design pattern is shown in the following
diagram:
Product Creator
factoryMethod()
anOperation() o ..
"
.~ Product = factoryMethod(}
The participants of the Factory Method design pattern are the following:
■ Creator (e.g., Scribble), which defines one or more fac tory methods (e.g.
makeCanvasListener ()) that create abstract products, that is, objects of type
Product. The Creator may provide a default implementation (e.g., the implemen-
tation of the makeCanvasListener O method in the Scribble class) and may
call factory methods to create Product objects (e.g., invocation of the makeCan-
vasListenerO method in the constructor of the Scribble class).
■ ConcreteCreator (e.g., DrawingPad), which overrides the factory method to
return an instance of a ConcreteProduct (e.g., implementation of the make-
CanvasListener () method in the DrawingPad class).
Factory Method and Factory (see Section 7.4.3 [p. 296]) are different design patterns.
The Factory design pattern involves a factory class whose sole responsibility is to
create objects. The Factory Method is for a class to defer the creation of certain objects
to its subclasses.
The go_al of this iteration _is rather simple. We want to further enhance the drawing pad
by addmg tools for drawmg filled ovals and rectangles. An ad hoc extension is rather
straightforward, but it is inelegant and difficult to maintain. We refactor the design of
the TwoEndsTool class to make it more extensible.
'
First, we define two new shapes, Filled0valShape and FilledRectangleShape,
by extending OvalShape and RectangleShape, respectively:
Classdraw2.FilledRectangleShape
package draw2 ;
import j ava.awt.*;
import drawl . *;
public class FilledRectangleShape extends RectangleShape {
public void draw(Graphics g) {
int x = Math.min(xl, x2);
int y = Math.min(yl, y2);
int w = Math . abs(xl - x2) + 1 ;
int h = Math . abs(yl - y2) + 1;
if (color != null) {
g . setColor (color);
}
g . fillRect(x, y, w, h) ;
}
}
Drawing filled rectangles and ovals is very similar to drawing unfilled rectangles and
ovals. A simple ad hoc approach to support the drawing of filled rectangles and ovals
is to modify the TwoEndsTool as follows :
case FILLED_OVAL: II . . .
case FILLED_RECT : II .. .
}
}
In this approach, modification is required for each new shape. The relevant code
segments for each shape are scattered in three different methods: mousePressed (),
mouseDragged (), and mouseReleased O. Modifications to these methods must
be carefully coordinated because the shapes to be drawn are tightly coupled with the
two-ends tool. It is rather cumbersome and error-prone to add new shapes using this
approach. Adding new shapes would be much simpler if the shapes to be drawn could
be decoupled from the two-ends tool.
Figure 9.16
draw1 .DrawingPad
The design of the
drawing pad-
iteration 5. draw2.DrawingPad Toolkit *
Tool
AbstractToaf
LineShape FilledRectangleShape
I
OvalShape FllledOvalShape
RectangleShape
9.6 Iteration 5: More Drawing Tools • 451
A better design is to separate the shape from the tool for drawing that shape
using the Strategy design pattern. The TwoEndsShapeTool class, instead of us-
ing an integer value to indicate the shape to be drawn, contains a reference to
TwoEndsShape, which represents the abstract strategy; the classes that implement
the TwoEndsShape interface represent the concrete strategies. Using the Strategy
pattern, the switch statements that are tightly coupled with the shapes are eliminated.
The TwoEndsShapeTool class does not contain code that is specific to any particular
shape. The design is shown in Figure 9.16
The prototype object serves as a representative of the shape to be drawn. The
mousePressed () , mouseDragged () , and mouseReleased () methods delegate
actions specific to each shape to the prototype object. The new shape object to be
added to the drawing is created by cloning the prototype object.
The main application class draw2. DrawingPad overrides the ini tTools () method
to include two additional drawing tools for filled ovals and filled rectangles. No other
changes are necessary.
In this iteration, we further enhance the drawing pad by adding a different type of
tool: a tool for typing text using the keyboard. A screen shot of the new drawing pad
is shown in Figure 9.17. The design is shown in Figure 9.18 The key issues addressed
in this iteration are handling keyboard input and keyboard focus.
Figure 9.17
, Ale Tools Option
The drawing pad-
iteration 6. Salbble
Java is easy!
Line Java is easy!
Java 1 s easy !
AIied Oval
AIied Rect
454 • Design Case Study: A Drawing Pad
TwoEndsShapeTool
LineShape FilledRectangleShape
OvalShape FilledOvalShape
RectangleShape
The draw3. Text class is a subclass of the Shape class representing a text string.
The field text represents the keys typed. The field font indicates the font to be used
to draw the text. The fields x and y represent the location of the text.
protected int x;
protected int y;
protected String text;
protected Font font;
The Tool interface defined earlier is intended to handle the input from a mouse. It is
inadequate to handle input from a keyboard. To define tools that handle key presse
from a keyboard, we introduce an extended interface KeyboardTool.
-
The TextTool class represents a concrete tool that handles input from the
keyboard. The text field stores the characters that have been typed. Because the
text field needs to be updated whenever a key is pressed, we use StringBuffer
instead of String for the sake of efficiency. The behavior of the text tool is as follows:
■ A mouse press indicates the position in the canvas where the text will be dis-
played.
■ Whenever a key is pressed, the corresponding character is appended to text,
and it is drawn in the canvas.
This way of handling keyboard input is rather primitive. It does not support editing
capabilities, such as deleting characters and moving the cursor.
The event listener of the canvas must also be extended to receive and handle the
events originating from the keyboard. The listener interface for keyboard events is
KeyListener. The method keyPressed () of a key listener is invoked when one or
more keys on the keyboard are pressed. In the following implementation, if the current
tool is an instance of KeyEventTool , the keyPressed () method of the current tool
is invoked; otherwise, the key press is ignored:
When dealing with input from the keyboard, we face another complication: key-
board focus. Often several windows are open on the screen, and multiple components
could accept input from the keyboard. However, only one component will receive
keyboard input at any moment: the component that currently has the keyboard focus.
The component having the keyboard focus is determined by a focus manager, which
provides a set of conventions for navigating the focus . In our drawing pad, we specify
that whenever the mouse is clicked on a component, that component will have the
458 • Design Case Study: A Drawing Pad
keyboard focus. Therefore, clicking the mouse on the canvas ensures that the canvas
has the keyboard focus. The implementation of the method mouse Cl i eked () of the
event listener of the canvas accomplishes this purpose by requesting the keyboard
focus when a mouse click event occurs on the canvas.
In the canvas class, in order to receive the keyboard input, it is necessary to define
the isFocusable () method to return true.
Chm drav3.K•~•v~anvaa
package draw3;
import java.awt.•;
i mport java.avt . event . *;
import java. util . EventListener;
import drawl . •;
public class KeyboardDrawingCanvas extends DrawingCanvas {
public KeyboardDrawingCanvas() {
addKeyListener((KeyListener) listener);
font= new Font(fontFamily, fontStyle, fontSize) ;
}
II factory method
protected EventListener makeCanvasListener() {
return (drawingCanvasListener =
new KeyboardDrawingCanvasListener(this));
}
The main application class draw3. DrawingPad adds the new text tool to the toolkit
and adds a cascading menu to the menu bar for selecting the font family, font size,
and font style:
II factory method
protected ScribbleCanvas makeCanvas() {
return (drawingCanvas = keyboardDrawingCanvas =
new KeyboardDrawingCanvas());
}
460 • Design Case Study: A Drawing Pad
int[] fontSizes = {
8, 10, 12, 16, 20, 24, 28, 32, 40, 48, 64
};
String[) fontStyleNames = {
"plain",
"bold",
"italic",
"bold italic"
};
inti;
ActionListener fontFamilyAction = new ActionListener() {
public void actionPerformed(ActionEvent event) {
Object source= event .getSource();
if (source instanceof JCheck.BoxMenuitem) {
JCheckBoxMenuitem mi= (JCheckBoxMenuitem) source;
String name= mi.getText();
keyboardDrawingCanvas.setFontFamily(name) ;
}
}
};
JMenu fontFamilyMenu = new JMenu("Font family");
ButtonGroup group= new ButtonGroup();
for (i = O; i < fontFamilyNames.length; i++) {
JCheckBoxMenultem mi=
new JCheck.BoxMenultem(fontFamilyNames[i]);
fontFamilyMenu.add(mi);
mi . addActionListener(fontFamilyAction);
group . add(mi);
}
optionMenu . add(fontFamilyMenu);
ActionListener fontSizeAction = new ActionListener() {
0
try {
keyboard.DrawingCanvas . setFontSize(
Integer . parseint(size));
} catch (NumberFormatException e) {
e.printStackTrace() ;
}
}
}
};
JMenu fontSizeMenu = new JMenu("Font size") ;
group= new ButtonGroup();
for (i = O; i < fontSizes.length; i++) {
JCheckBoxMenuitem mi=
new JCheckBoxMenuitem(Integer.toString(fontSizes[i]));
fontSizeMenu.add(mi) ;
mi . addActionListener(fontSizeAction);
group.add(mi) ;
}
optionMenu . add(fontSizeMenu);
ActionListener fontStyleAction = new ActionListener() {
public void actionPerformed(ActionEvent event) {
Object source= event.getSource() ;
if (source instanceof JCheckBoxMenuitem) {
JCheckBoxMenuitem mi= (JCheckBoxMenuitem) source;
String styleName = mi .getText();
int style= Font.PLAIN;
if (styleName .equals("bold")) {
style= Font . BOLD;
} else if (styleName.equals ("italic")) {
style= Font.ITALIC;
} else if (styleName . equals ( "bold itali c") ) {
style= Font.BOLD I Font.ITALIC ;
}
keyboardDrawingCanvas.setFontStyle (style);
}
}
};
JMenu fontStyleMenu = new JMenu("Font styl e" ) ;
group= new ButtonGroup();
for (i = O; i < fontStyleNames.length; i++) {
JCheckBoxMenuitem mi=
new JCheckBoxMenuitem(fontStyleNames[i]);
fontStyleMenu.add(mi );
mi . addActionListener(fontStyleAction);
group.add(mi) ;
}
optionMenu.add(fontStyleMenu) ;
}
CHAPTER SUMMARY
FURTHER READINGS
PROJECT
9.1 Use lhe iterative development approach lo add (c) Support selecting image files as the back-
ome of the following enhancements to the ground.
drawing pad program: (d) Support a zoom-in and zoom-out function.
(a) Add new drawing tools to draw different (e) Support moving and deleting shapes.
shapes, such as diamonds, polygons,
splines, and so on.
(b) Support selecting different background
colors. Refactor the code to avoid code
duplication with the selecting-pen-colors
function.
CHAPTER-10
CHAPTER OVERVIEW
In this chapter, we first present an idiom for type-safe enumeration types. We then discuss
a number of important design patterns, including Abstract Factory, Prototype, Builder,
Command, and Adapter. These design patterns will be illustrated in the context of several
complete programs.
In this chapter, we use a simple maze game as shown in Figure 10. l to illust_rate_an
implementation idiom and a number of important design patterns. The following is a
very brief description of the maze game:
th
The board of the maze game is an n x m grid. Figure 10.1 shows two instances of e
maze game: a small, 1 x 2 maze game, and a large, 3 x 3 maze game. Each square on
the board is a room. Adjacent rooms are eparated by either a wall or a door between
them. The doors can be either open or closed. Each maze game contains a player
through open doors.
represented by a dot. The player can move from room to room
Variou aspects of the game will be further elaborated later on.
465
466 • More Design Patterns
Figure 10.1
A simple maze
game.
Before we discuss the design of the maze game, we first present a useful implemen-
tation idiom for enumeration types in Java. An enumeration type consists of a finite
set of distinct values. To implement the maze game, we need to deal with a couple of
simple enumeration types:
■ Orientation, with values of horizontal and vertical. A wall or a door on the maze
board is in either horizontal or vertical position.
■ Direction, with values of north, south, east, and west. Each room has four sides
one in each direction. '
However, the following statements clearly make no sense, but are legal:
orientation= Direction.WEST;
direction= Orientation .HORIZONTAL;
orientation= 6;
direction= -1;
In this approach, variables and values of different enumeration types are interchange-
able, and the values may not even be meaningful. Second, the values of enumeration
types are numbers. When directly printing these values, they are cryptic. For example,
This prints out the following message, which is cryptic and unreadable:
To make the printout more readable, the printing of values must be handled as follows :
II . . .
direction= Direction . EAST;
System . out.println("The current direction is : 11 +
Orientation.name(direction)) ;
468 • More Design Patterns
In Java there is a better approach to implementing enumeration types using the type-
safe e11 ;,meration idiom, as illustrated in the following definition of the enumeration
type Orientation:
Enumeration~ aaze. Orientation
package maze;
public class Orientation {
public static final Orientation VERTICAL=
new Orientation ("Vertical");
public static final Orientation HORIZONTAL=
new Orientation ("Horizontal") ;
public String toString() {
return name;
}
Using this idiom, each enumeration type is defined as a class. Each value of the
enumeration type is associated with a descriptive name rather than an integer value.
Each value of the enumeration type is defined as a constant that references an instance
of the class. The toStringO method returns the descriptive name of each value.
The constructor of the class is private. It prevents any other instances of the class
from being created outside the class, since an enumeration type consists of only a
fixed finite number of distinct values. In other words, the instances referenced by the
constants defined in this class are the only instances of this class in existence. This
idiom ensures that
• each enumeration type may only have a fixed number of distinct values.
• the enumeration types are type-safe; that is, variables and values of different
'
enumeration types are not interchangeable.
• the values can be printed with descriptive names.
For example,
Orientation orientation= Orientation . VERTICAL;
System.out.println("The current orientation is: " + orientation);
Furthermore, since the enumeration type has only a fixed number of distinct
values, equality of two values (in lances) i the ame as the identity of the two value
10.1 Type-Safe Enumeration Types • 469
(instances). So, identity comparison, that is, equality of references, can be used for
comparing the equality of values:
Orientation orientationl =
Orientation orientation2 =
if (orientationl == orientation2) {
// both have the same orientation
}
In this ordered enumeration type, each value is also associated with an ordinal
number, which is automatically assigned when the instances are created. This class
implements the Comparable interface and defines the compareTo () method to
compare with other instances of this class based on the ordinal numbers.
This class also provides a number of methods that are specific to handling the
directions: the opposite () method returns the opposite direction, and the first ()
and next() methods support iteration through the directions, as illustrated here:
for (Di rection dir = Direction .first (); dir != null; dir = dir .next ()) {
// process each direction
}
In this section, we present a simple design of the maze game that supports only a
default style of the maze game. The design is shown in Figure I 0.2. Later, we present
several improved designs using various design patterns to support different styles of
the maze game.
Figure 10.2
rooms 1
Maze
The design of the I
I
simple maze game. 1 .. 2 : create
I
, create :
----------------------- ----- ---------~
10.2 Creational Design Patterns ■ 4 71
package maze;
import java . awt . • ;
public interface MapSite extends Cloneable {
public Object clone O throws CloneNotSupportedException;
public void enter(Maze maze);
public void draw(Graphics g, int x, int y, int w, int h);
}
The Room class represents rooms on the maze board. Each room has a room
number. Each room also has four sides, which must be either walls or doors. When a
player enters a room, a sound will be played.
l. Some of Lhe details of drawing 1he maze board are not essen1ial in illustrating Lhe de ign pattern and
are thus omitted in the ource code Ii tings that follow. The complete source code is available in the
supplement package.
472 • More Design Patterns
if ( i nroom) {
g . setColor(PLAYER_COLOR) ;
g. f ill□val(x + w / 2 - 5, y + h / 2 - 5, 10, 10);
}
}
The Door class represents doors on the maze board. Each door connects two
rooms, one on each side. When the player tries to enter a door that is open, the player
will enter the room on the other side of the door. When a player tries to enter a door
that is closed, the player will remain in the current room and a smashing sound will
be played.
I
public Room otherSideFrom(Room room) {
if (room!= null) {
if (room== rooml) {
return room2;
} else if (room== room2) {
return rooml;
}
}
return null;
}
public void enter(Maze maze) {
if (open) {
Room otherRoom = otherSideFrom(maze.getCurrentRoom());
if (otherRoom != null) {
otherRoom.enter(maze);
}
} else {
ding .play();
}
}
public void draw(Graphics g, int x, int y, int w, int h) {
g.setColor(Wall.WALL_COLOR);
g.fillRect(x, y, w, h);
if (orientation== Orientation.VERTICAL) {
y += 2 * w; h -= 4 * w;
} else {
x += 2 * h; w -= 4 * h;
}
if (open) {
g.setColor(Room.ROOM_COLDR);
g.fillRect(x, y, w, h);
} else {
g.setColor(Color.red);
g.fillRect(x, y, w, h);
g.setColor(Color .black);
g.drawRect(x, y, w, h);
}
}
protected Room rooml ;
protected Room room2;
protected boolean open;
protected Orientation orientation ;
protected static AudioClip ding=
util .AudioUtility .getAudioClip (" audio/ding . au");
-
10.2 Cn~atlonal Design Patterns • 4 75
The Wall class represents walls on the maze board. A wall could be between two
rooms or on the edge of the maze board. Walls are impassable. So when a player tries
to enter a waJl, the player will remain in the current room and a smashing sound will
be played.
package maze;
package maze;
import java .util.•;
import java.awt .•;
import java . awt .event . • ;
import javax .swi ng . •;
476 • More Design Patterns
}
setCurrentRoom(room);
}
view. repaint() ;
I
}
}
protected List rooms = new ArrayList (); // the list of rooms that comprise
// the board
protected Room curRoom = null; // the room in which the player is
10.2 Cn~ational Design Patterns • 4 77
The SimpleMazeGame class is the main driver of the maze program. It is respon-
sible for creating the layout of maze games. The createMaze () method creates a
small maze board with two rooms, and the createLargeMaze O method creates a
large maze board with nine rooms. Note that the code to create a large maze game is
lengthy, ad hoc, and hard to change.
r·
• Creates a 3 x 3 large maze board with 9 rooms.
·1
public static Maze createLargeMaze () {
Maze maze= new Maze( ) ;
Room rooml = new Room(1 );
Room room2 = new Room(2) ;
Room room3 = new Room (3);
4 78 • More Design Patterns
The Themes
To make the maze game more interesting, we want to support different looks or
themes of the game. In different themes, the rooms, walls, and doors could be drawn
in different shapes or colors, and the reactions to entering rooms, walls, and door
480 ■ More Design Patterns
HarryPotterRoom .---.---, __
..__Room _,,:;.~-;---, SnowWhiteRoom
could also have different sound or animation effects. Each theme consists of a set of
rooms, walls, and doors. For the purpose of illustration, we build two new themes of
the maze game in addition to the default theme: a Harry Potter theme and a Snow
White theme. For each theme, a set of subclasses of room, wall, and door are defined.
The subclasses of different themes are placed in separate packages; see Figure 10.3.
The classes for the Harry Potter theme are in a subpackage named maze. harry.
The rooms, doors, and walls in the Harry Potter theme are represented by classes
HarryPotterRoom, HarryPotterDoor, and HarryPotterWall, respectively.
Clatsaaze.h!!!I.HarryPotterRoom
package maze .harry;
import java.awt.•;
import java . applet . AudioClip;
import maze .•;
class HarryPotterRoom extends Room {
public static final Color ROOM_COLOR = new Color(85, 107, 47);
public static final Color PLAYER_COLOR = Color . black;
public HarryPotterRoom(int roomNumber) {
super(roomNumber);
}
. 7
10.2 Creational Design Patterns • 481
}
482 • More Design Patterns
The classes for the Snow White theme are in a subpackage named maze. snow.
The rooms, doors, and walls in the Snow White theme are represented by classes
SnowWhiteRoom, SnowWhi teDoor, and SnowWhi teWall, respectively.
Clals111.Ze.nov.SnovWhiteDoor
package maze.snow;
import java.awt .•;
import maze.•;
class SnowWhiteDoor extends Door {
public SnowWhiteDoor(Room rooml I Room room2) {
super(rooml, room2);
}
Now we have defined the classes in the new themes. The question is how to
make the themes easily interchangeable without affecting other aspects of the maze
game, and to make it easy to add additional themes. Switching between themes means
replacing one set of rooms, walls, and doors from one theme with another set of
rooms, walls, and doors from another theme. A straightforward solution is to create
the maze boards of different themes in a way similar to the createMaze () and
createLargeMaze () in SimpleMazeGame [p. 477). Using the small maze board
as an example, the maze boards in the two new themes can be created as follows:
Abstract Factory is a creational design pattern for creating a set of related and compati-
ble products from several interchangeable product frunilies . The product instances are
not created using the new operator. They are created using a set of methods defined in
a factory class, which represents a product family. The different factories representing
10.2 Cn~ational Design Patterns • 485
The structure of the Abstract Factory design pattern is shown in the following
diagram:
AbstractFactor Client
makeProductA ()
makeProductB()
AbstractProduct
The participants of the Abstract Factory design pattern are the following:
■
AbstractFactory (e.g., MazeFactory), which defines methods that create ab-
stract products (e.g. makeRoom () , make Wall O , makeDoor O) and may pro-
vide default implementation.
■
ConcreteFacto,y (e.g., HarryPotterMazeFactory, SnowWhiteMazeFac-
tory), which implements the methods to create c~ncrete instances of Con-
creteProduct. Each ConcreteFactory creates products m the same product family.
■ AbstractProduct (e.g., Room, Wall, Door), which defines an interface for a type
of product and may provide default implementation.
■ ConcreteProduct (e.g., HarryPotterRoom, HarryPotterWall, HarryPot-
terDoor, SnowWhi teRoom, SnowWhi teWall, SnowWhi teDoor), which de-
fines a product to be created by the corresponding concrete factory and imple-
ments the AbstractProduct interface.
■ Client (e.g., Maze), which uses only AbstractFactory and AbstractProduct.
In the SimpleMazeGame class [p. 477] , map site objects are created using the
new operator. So it is inflexible and inextensible. Using the Abstract Factory design
pattern, map site objects in different themes can be created using interchangeable
factories for the themes. The design of the maze game using the Abstract Factory
design pattern is shown in Figure 10.4.
The abstract products in this design of the maze game are the map site classes
Room, Wall, and Door. More precisely, these are abstract products with default im-
plementations. The concrete products are the map site classes defined in the different
themes, that is, HarryPotterRoom, HarryPotterWall, and HarryPotterDoor in
the Harry Potter theme, and SnowWhi teRoom, SnowWhi teWall, and SnowWhi te-
Door in the Snow White theme. Each theme represents a product family. The Maze-
Factory class defines the abstract factory. It also provides a default implementation
that returns the map site objects in the default theme.
.....
10.2 Creatlonal Design Patterns • 487
Figure 10.4
sides
A design of the
maze game using
the Abstract Fac-
tory pattern.
I I
I.
Maze Factory ~· ~ · - · - · - · - · - · i . _ __ __ _
create create
Maze makeMaze() ---- - -- - _ 1
Wall makeWall()
Room makeRoom()
Door makeDoor()
fl,.
Create :
I
'r I
: create
'
I
HarryPotterMaze Factory SnowWhiteMazeFactory
I
I
Maze makeMaze() Maze makeMaze()
I
Wall makeWall() Wall makeWall()
I
Room makeRoom() Room makeRoom()
I
Door makeDoor() Door makeDoor()
I
A 7:-.
~ use ~ use I use
MazeGameAbstractFactory
1 - - - - - - - - - -- - - - - - 1 build
Maze createMaze(MazeFactory factory) 1 - - - - - - - - - '
room3.setSide(Direction.NORTH, factory.makeWall());
room3 . setSide(Direction.EAST, door2);
room3.setSide(Direction . SOUTH, factory.makeWall());
room3.setSide(Direction .WEST , factory .makeWall() ) ;
room5.setSide(Direction.NORTH, door5) ;
room5.setSide(Direction.EAST, door3);
room5.setSide(Direction.SOUTH, factory .makeWall ());
room5.setSide(Direction.WEST, door4);
room6.setSide(Direction.NORTH, door6) ;
room6.setSide(Direction.EAST, door4) ;
room6.setSide(Direction . SOUTH, factory.makeWall());
room6.setSide(Direction .WEST, factory .makeWall() ) ;
....._ -
1 O. 2 Cn~ational Design Patterns • 4 91
An alternative solution, which is similar to the Abstract Factory pattern, is to use the
Factory Method pattern discussed in Section 9.5 .6 [p. 447]. The two solutions are
similar in the sense that both define methods (factory methods) for creating instances
of products, and different themes are represented by subclasses that override the
factory methods to create concrete products in respective themes. There are also
differences between the two patterns. The sole responsibility of the factories in the
Abstract Factory pattern is to create products. However, creating products is only one
of the responsibilities of the creators in the Factory Method pattern. The creators in the
Factory Method pattern are also responsible for building a structure using the products
created by the factory methods. For example, in the maze game, the Abstract Factory
pattern separates the responsibilities for creating the map sites, that is, the parts, and
building the maze board using the parts, and assigns them to two classes. The Factory
Method pattern, however, combines these two responsibilities into one class- the
MazeGameCreator class.
The design of the maze game using the Factory Method design pattern is shown
in Figure I 0.5. The MazeGameCreator class is the creator in the Factory Method
Figure 10.5
MapSite ~--------,sides
A design of the
maze game using 1..2
the Factory Method
- - - ·- ·- ·- ·- -- -- -
pattern. 1
rooms
HarryPotterDoor HarryPotterWall HarryPotterRoom
............. ·:· ..i·....... ~-.... ............... --~-...... ·j· . ... -·-- ....... -~- ............... :
' '
~
I I . I
1
' • I I
, I ~ - - ------, , I
I i - MazeGameCreator ·'- · - · - -- -- · - · 7·-·-·-·-·
Maze createMaze()
Maze makeMaze() build
Wall makeWall()
Room makeRoom()
Door makeDoor()
create : create
HarryPotterMazeGameCreator SnowWhiteMazeGameCreator
doorl.setOpen(true);
door2.set0pen(false);
door3 . set0pen(true);
door4 . set0pen(true);
door5 . set0pen(false);
door6 .set0pen(true);
door7.set0pen(true);
door8.set0pen(true);
... ..
10.2 Creational Design Patterns ■ 493
return maze;
}
}
10.2 Creational Design Patterns • 495
One of the shortcomings of the Abstract Factory and Factory Method patterns is that
each product family is represented by a subclass. Therefore, if there are many product
families, there will also be many subclasses. Furthermore, if it is possible to mix
products from different product families, then the potential number of combinations
and the number of subclasses could explode. An alternative solution that would
prevent the explosion of the number of subclasses is to use the Prototype pattern.
Using the Prototype pattern, objects are created by cloning a set of prototypes. Objects
in different product families can be created by using a different set of prototypes.
Using the Prototype pattern, product families can be defined or altered at run time by
choosing the desired prototype. While using the Abstract Factory or Factory Method
patterns, product families are defined in the form of a class and thus cannot be altered
or configured at run time.
The structure of the Prototype design pattern is shown in the following diagram:
\ Cloneable
ZSI
I
I
Client
I
I prototype
Prototype -- aMethod() 9
clone()
/ ~
p = prototype.clone()
clone() clone()
The design of the maze game using the Prototype design pattern is shown in Fig-
ure l 0.6. This design is also an extension of the design using the Abstract Factory
panern. The MazePrototypeFactory is a concrete factory in the Abstract Factory
pattern, and it creates the map site objects using the Prototype pattern. It contains a set
of prototypes of the concrete map sites to be created. The concrete map site objects
are created by cloning the corresponding prototypes. To allow the cloning of the pro-
totypes, the concrete product classes must implement the j ava . lang. Cloneable
interface and define a public clone O method (see Section 6.3.4 [p. 231 ]).
10 .2 creational Design Patterns • 497
Figure 10.6
C/oneable
A design of the
maze game using
MapSite
the Prototype pat- 4
tern. clone() -- sides
~
I 1..2
I 0 *
Door Wall Room -
I Maze I f:..
MazeFactory
Maze makeMaze()
Wall makeWall()
Room makeRoom()
Door makeDoor()
1. Command-line argument
2. Operating system environment variable
3. Java properties
In the previou example the configuration, or the theme, i et u ing command line
arguments. Most operating ystem allow users to et g~obal propert_ies in environ-
ment variables, which are acce ible to all program . Envuonment variables are often
sed in CIC++ and other programs. Although Java applications can also access en-
~ironrnent variables, this approach i often avoided because environment variables
are platform dependent. Java program often use properties to configure programs.
500 • More Design Patterns
Properties are represented as a mapping whose ke~s an~ values are all strings. Prop-
erties can be stored in a text file, which can be easily edited.
The following Uni versalMazeFactory class can build maze games with var-
ious schemes and using various styles of factories. It selects the scheme and factory
based on the following two properties:
■ maze . theme indicates the theme of the game board, one of Harry, Snow, or
Simple.
■ maze . prototype indicates whether to use prototype-based factory, either true
or false .
These properties can be specified in two ways:
1. In a properties file named maze. properties. The properties can be specified
in the properties file as follows:
maze.theme=Harry
maze .prototype=true
if (factory,_ null) {
factory• new MazeFactory();
}
thelnstance •
new HazePrototypeFactory(factory.makeMaze () ,
factory.makeWall () ,
factory.makeRoom (O),
factory.makeDoor(nuli, null));
} else {
switch (theme) {
case HARRY_PORTER_THEHE:
thelnstance: new maze .harry .HarryPotterHazeFactory ();
break;
case SNOW_WHITE_THEME :
thelnstance • new maze.snow . SnowWhiteHazeFactory ();
break;
default:
thelnstance = new HazeFactory();
break ;
}
}
}
return thelnstance;
}
protected UniversalMazeFactory() {}
static {
Properties configProperties • new Properties ();
try {
configProperties.load(new FileinputStream ("maze .pr operties" ));
} catch (!□Exception e ) {
e .printStackTrace ();
}
String value ;
value• System.getProperty ("maze .theme");
if (value =z null ) {
value '"' configProperties .getProperty( "maze .theme ");
}
if (value !• null) {
if (" Harry" . equals (value)) {
theme • HAAAY_PORTER_THEHE ;
} else if ("Snow" .equals (value )) {
theme a SNOW_WHITE_THEME;
}
}
502 ■ More Design Patterns
value• System.getProperty("maze.prototype");
if (value•• null) {
value • configProperties. get Property ("maze. prototype") ;
}
if (value !• null) {
usePrototype • Boolean .getBoolean(value) ;
}
}
The structure of the Builder design pattern is shown in the following diagram:
Director A
Builder
/
ConcreteBuilder1
~
ConcreteBuilder2
I P~oduct I buildPart() buildPart()
The design of the maze game using the Builder design pattern is hown in
Figure 10.7. In this design, we define a builder interface MazeBuilder to build
I
SimpleMazeBuilder FactoryMazeBuilder
'
I MazeFactory I
504 • More Design Patterns
components of the maze board from the basic parts. The methods of the MazeBuilder
class are summarized in the following table:
Methods Description
Clallaae.lluehil4er-
package maze;
public interface HazeBuilder {
public void newMaze();
public Haze getMaze();
public void buildRoom(int roomNumber);
public void buildDoor(int roomNumberl, int roomNumber2,
Direction dir, boolean open);
}
a. .... a lellazeBuilder
package maze ;
public class SimpleMazeBuilder implements MazeBuilder {
public void newHaze() {
maze= new Maze();
}
The FactoryMazeBuilder class uses a factory to construct the map site objects,
so that different themes can be supported.
Clallaaze.Facto~eBuilder
package maze;
public class FactoryMazeBuilder implements MazeBuilder {
public FactoryMazeBuilder (MazeFactory factory) {
this . factory= factory ;
}
The structure of the Command design pattern is shown in the following diagram:
I Client I I lnvoker IA
r-,, - Command
ereate : execute()
..
Receiver
receiver
-- ConcreteCommand
actionO
·.. .......... ..... - .... . ......... . . .... . . .;:, execute()
9
receiver.action()
The design of the maze game with undoable moves using the Command pattern is
shown in Figure 10.8.
Cllllllaaze,Coaaall4
package maze;
public interface Command {
public void execute() ;
}
Maze MazeMoveCommand
······•·"···
move() • execute()
1
doCommand(Command c) A -
undo()
V
undoCommand() moves
51 0 • More D~ign Patterns
a.. ....11u.
public class Maze implements Cloneable {
(Methods for building the maze board. See Section 10.2.1 [p. 475]}
public void move(Direction direction) {
if (cu.rRoom != null) {
MapSite side= cu.rRoom . getSide (direction);
if (side != null) {
side.enter(this) ;
}
}
}
break;
case KeyEvent · VK_LEFT: Direction. WEST);
commands new MazeMoveCommand(maze,
break;
case KeyEvent.VK_RIGHT:
command. new MazeMoveCommand(maze, Direction.EAST);
break;
}
if (command != null) {
maze .doCommand(command);
}
}
Haze maze;
}
package maze;
import java.awt.•;
import java.awt .event.•;
import javax.swing.•;
public class UndoableMazeGame {
public static void main(String[] args) {
Maze maze;
MazeBuilder builder;
MazeFactory factory= null;
if (args.length > 0) {
if ("Harry".equals(args[O])) {
factory= new HarryPotterMazeFactory();
} else if ("Snow" . equals (args [OJ)) {
factory s new SnowWhiteMazeFactory();
} else if ("Default".equals(args[O])) {
factory• new MazeFactory();
}
}
if (factory !• null) {
builder• new FactoryMazeBuilder(factory);
} else {
builder= new SimpleMazeBuilder();
}
maze= MazeGameBuilder.createMaze(builder);
maze.setCurrentRoom(l);
10.4 Structural Patterns • 513
'•
The Adapter design pattern is another very commonly used design pattern. We often
find some classes that provide functionality that can be reused. However, the interface
providing the functionality is not the interface expected by the client. The Adapter
pattern addresses the issue of adapting an interface to accommodate the needs of a
client that hopes to reuse the functionality but expects a different interface.
■ Class adapter, which relies on inheritance. The structure of the class adapter is
shown in the following diagram:
do Task() periormTask()
Adapter
do Task() periormTask()
~
I
I i
: ~ adaptee
Adapter
doTask() 0 . .. .. .. . . adaptee.periormTask()
■ Target (e.g., TableEntry), which defines the interface used by the Client .
■ Client (e.g., Table), which uses objects conforming to the Target interface.
■ Adaptee (e.g., Student), which defines the interface of an existing class to be
reused.
■ Adapter (e.g., StudentEntry, StudentEntry2), which adapts the interface of
Adaptee to Target.
Figure 10.9
T GP~
The table of entries 80108 103-367- ◄ 105 2. 1
134191 '156-303-8166 2.5
of student informa- 71126 230-525-18◄9 3. 1
information is shown in Figure 10.9. When you click on the header of a column, the
list of objects will be sorted based on the values in that column.
Our table class, adapter. Table, extends the JTable class in swing. The design
of the generic table is shown in Figure I 0.10.
The key to the design of the generic table is that the data entries shown in the
table must be instances of a class that conforms to the TableEntry interface.
Figure 10.1 O 1 1
javax.swing.JTable10- -----,;:il't javax.swing.table.TableModel
Table.ListTableModel TableEntry
Table
516 • More Design Patterns
Each entry represents a row in the table. The methods of the TableEntry
interface are summarized in the following table:
Methods Description
The implementation of the Table class is shown in the following code. However,
the implementation of the Table class is immaterial to the discussion of the Adapter
pattern.
a- a48l~er. Table
package adapter;
import java.awt.Color;
import java.awt.Point;
import java.allt .event .•;
import javax.swing.• ;
import javax.swing.table.•;
import javax.swing. event . •;
import java.util.•;
public class Table extends JTable {
public Table() {
this (null) ;
}
getTableHeader().addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
Point p = e.getPoint();
JTableHeader header= (JTableHeader) e.getSource();
int column= header.columnAtPoint(p) ;
if (model.sort(column)) {
clearSelection();
updateUI O ;
}
}
}) ;
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
5 20 • More Design Patterns
Now, given the following Student class, we want to display the student infor-
mation using the generic table.
ter.Stud•nt
package adapter ;
public class Student implements Cloneable {
public Student () {}
public Student(String ID,
String firstName,
String lastName,
String streetAddress,
String state,
String city,
String country,
String postalCode,
String telephone,
float GPA,
int totalCredits) {
this . ID = ID ;
this.firstName = firstName;
this . lastName = lastName;
this . streetAddress = streetAddress ;
this.state= state;
this.city= city;
this . country= country;
this . postalCode = postalCode;
this . telephone= telephone;
thi s .GPA= GPA ;
this.totalCredits = totalCredits;
}
The problem is that the Table class expects the entries to be instances of a
class that confonns to the TableEntry interface, while the Student class does not
conform to the TableEntry interface. Instead of rewriting the Student class to
make it conform to the TableEntry interface, we can use the Adapter pattern. We
can use either form of the Adapter to accomplish the task. The design using the class
adapter form is shown in Figure 10.1 l. The StudentEntry class is the adapter, the
Student class is the adaptee, and the TableEntry interface is the target.
1gure 10.11 1 •
1 1 ~-~ TableEntry Student
Table n-- -;:ll>j Table.ListTableModel
lass adapter: ,_____ _
I
adapt by lnherf- I
I
ance.
StudentEntry
5 24 ■ More Design Patterns
= 8;
public static final int TELEPHONE_COLUMN
public static final int GPA_COLUMN = 9;
public static final int TOTAL_CREDITS_COLUMN = 10;
String telephone,
float GPA,
int totalCredits) {
super(ID, firstName, lastName, streetAddress , state, city,
country, postalCode, telephone, GPA, totalCredits);
}
The design using the object adapter form is shown in Figure 10.12. The
StudentEntry2 class is the adapter, the Student class is the adaptee, and the
TableEntry interface is the target.
a. .StuclatEnt
package adapter;
import java.util.Comparator;
r
• Adapter design pattern
·1
public class StudentEntry2 implements TableEntry {
II position of each column
public static final int ID_COLUMN = O;
public static final int FIRST_NAME_COLUMN = 1;
public static final int LAST_NAME_COLUMN = 2;
public static final int STREET_ADDRESS_COLUMN = 3;
public static final int STATE_COLUMN = 4;
public static final int CITY_COLUMN = 5;
public static final int COUNTRY_COLUMN =6;
public static final int POSTAL_CODE_COLUMN = 7;
public static final int TELEPHONE_COLUMN = 8;
public static final int GPA_COLUMN = 9;
public static final int TOTAL_CREDITS_COLUMN = 10;
1 1 1 ..
Figure 10.12 Table JC>-- -::l"I Table.listTableModel > - -~ Tab/eEntry Student
1
Object adapter:
dapt by delega- 1
on. StudentEntry
Student
528 • More Design Patterns
The Main class creates a list of instances of the Student class and displays the
instances using the generic table via either of the adapters. The adapters are selected
by an optional command-line argument. If the command-line argument Delegation
is present, the object adapter will be used; otherwise, the class adapter will be used.
a..~.llaia
package adapter;
The Composite design pattern was discussed in Section 8.3.2 [page 336]. Here, we
present another example to illustrate the Composite pattern.
The Composite pattern is used to build a hierarchical structure of objects. An
example of such a hierarchy can be found in variou e-mail programs:
Figure 10.13
Mailbox/tern
items
A design of the mail
application using 1
the Composite pat-
Mail MailFolder
tern.
:t•
package mail ;
public abstract class Mailboxltem {
public String getName() {
return name;
}
package mail;
import java.util . • ;
public i nt count () {
i nt count • O;
Iterator iterator: items.iterator();
while (iterator . hasNext ()) {
Object item • iterator.next();
if (item instanceof Mailboxitem) {
534 ■ More Design Patterns
0-..U.llail
package mail;
import java.util.*;
public class Mail extends Mailboxltem {
public Mail(String from,
String subject,
Date date,
MailPriority priority,
MailStatus status) {
this(from, subject, null, date, priority, status, null, null);
}
Two ordered enumeration types are used in the Mail class: MailPriori ty and
MailStatus.
a.. ..U.llailPriorit
package mail ;
public class HailPriority implements Comparable {
public static final HailPriority LOW = new MailPriority( 11 Low 11 ) ;
public static final HailPriority MEDIUM • new MailPriority("Mediwn");
public static final HailPriority HIGH = new MailPriority("High");
public static final HailPriority VERY_HIGH •
new HailPriority ("Very high") ;
public String toString() {
return name;
}
package mail;
public class HailStatus implements Comparable {
public static final HailStatus NEW • new HailStatus("New");
public static final HailStatus READ • new HailStatus("Read");
public static final HailStatus REPLIED • new HailStatus("Replied");
public static final HailStatus FORWARDED• new HailStatus("Forwarded");
The mail . Main class is a simple test of the mail manager program. It populates
the mailbox hierarchy by creating a number of mail folders, subfolders, and mails.
Then it gets a count of the total number of mails and the total number of new e-mail
messages in all folders, including nested subfolders, of the in box.
package mail;
new Hail( . . . ) ,
};
protected static Date getTime(int year, int month, int date, int hour,
int minute) {
Calendar calendar• Calendar . getlnstance() ;
calendar . set(year, month, date, hour, minute, O) ;
return calendar . getTime() ;
}
}
10.4 Structural Patterns • 539
Figure 10.14
lnbox
A mall application. f ·CJ Courses
t [)SE-ts
[)SE'452
ux -- a small OS
for final project
[)work
[)News
D Junk Mails
A GUI Implementation
The next step in developing the mail manager program is to build a GUI interface.
The hierarchy of folders is represented by a tree structure using the JTree class in
Swing. The mail in each folder is displayed using the generic table discussed in the
previous section. A screen shot of the GUI interface is shown in Figure 10.14. The
object Adapter pattern is used to adapt the Mail to TableEntry. The design is shown
in Figure 10.15.
The MailEntry class is the adapter, the Mail class is the adaptee, and the
TableEntry interface is the target.
Figure 10. 15 1 1 •
TableEntry
Table Table.ListTableModel
A design of the
GUI interface for
• MailEntry
the mall application MailboxItem
using the Adapter items 1
pattern.
1 mail
public static final int O columnWidths = { 50, 50, 100, 150, 200 } ;
public HailEntry(Hai l mai l) {
this .mail= mai l ;
}
switch (col) {
case PRIORITY_COLUHN: return mail . getPriority();
case STATIJS_COLUHN: return mail .getStatus() ;
case FROM_COLUHN: return mail.getFrom();
case RECEIVED_COLUHN: return mail .getDate();
case SUBJECT_COLUHN: return mail .getSubject();
}
}
return null;
}
The mail. gui. Main class constructs the GUI interface using a JTree object
for the folder hierarchy and a Table object for displaying the mail in the current
folder.
Clallaail..
package mail .gui;
import java. awt .Dimension;
import java.awt .Toolkit ;
i mport j ava.util .•;
i mport javax . swing .• ;
i mport javax . swing .tree . • ;
i mport j avax .swing.event.•;
import mail . • ;
i mport adapter . • ;
public class Mai n {
public static f i nal int INITIAL_FRAME_WIDTH • 800;
public static final i nt INITIAL_FRAME_HEIGHT • 400 ;
public stat ic void mai n(String[] args ) {
Mai lFol der i nboxFolder • mail . Main .buildinbox ();
10.4 Structural Patterns • 543
JTree tree;
JSplitPane splitPane ;
}
return root;
}
return null;
}
CHAPTER OVERVIEW
In this chapter, we introduce concurrent, or multithreaded, programming. We discuss the
mechanisms that Java provides to support concurrent prog@mming. We also address the
issues of synchronization and cooperation among threads.
THREADS
547
548 • Concurrent Programming
11 .1 .1 Creation of Threads
A thread is an instance of the java. lang. Thread class. A Thread object is also
known as an active object. The graphical notation for active objects is shown in
Figure 11.1.
Threads can be created and declared in one of two ways: by directly extend-
ing the j ava. lang . Thread class or by implementing the j ava. lang . Runnable
interface.
Figure 11.1
Thread Runnable
Graphical notation z;s
for active objects
(i.e., threads).
MyThread MyThread
runO run()
of the Thread class is a hook method, which must be overridden in the subclass. The
run () method defines the body of the thread and is similar to the main () method
of a sequential program. The run () method of a thread is invoked when execution
starts. Execution of a thread ends when the run () method returns. A template for
defining a new thread class is presented in the following program segment:
new MyThread().start();
Conversely, the run () method should not be invoked directly. Doing so would
cause the method to be executed in the thread of the caller, not in a new thread.
PURPOSE
This example demonstrates thread creation by extending the Thread class.
DESCRIPTION
The infinite counter starts from an initial value, and the counter will be incremented
by a specified amount at a given pace.
SOLUTION
The following is the thread class that implements the infinite counter. It extends the
Thread class. This class defines a thread whose body consists of an infinite loop. It
maintains a counter, which is incremented by the amount of inc in each iteration. Each
iteration of the loop sleep a duration pecified by the field delay in milliseconds.
550 • Concurrent Programming
Two threads are started in the main () method. Both start counting from 0, but in
different directions. The second thread, the one with a negative increment, receives
a longer delay. Note in the following output that the output from the two threads is
interleaved:
Execution of this program actually involves three threads: the thread that executes
the main() method and the twoCounter1 threads created inside the main() method.
The thread that executes the main () method terminates when the main () method
returns from creating the two Counter1 threads. The two Counter1 threads will run
concurrently and indefinitely until they are explicitly killed. ■
11.1 Threads • 551
PURPOSE
This is another example that demonstrates thread creation by extending the Thread
class.
DESCRIPTION
This program generates a series of quotes for the price of a given stock by simulating
the fluctuation of stock prices.
SOLUTION
The foUowing class implements an infinite loop that generates stock quotes. It sim-
ulates the fluctuation of stock prices using a random number generation function
Math. random () .
The program is started with the initial price set at 100. The following is the output:
100
101
104
103
102
101
•
Implementing the Runnable Interface
Defining threads by directly extending the Thread class is only one of the options.
Sometimes, however, it may be impossible to use this option, because Java only
supports single inheritance among classes. For example, applets are required to
extend the class j ava. applet. Applet. Thus applets cannot also extend the Thread
class at the same time. Java provides an alternative means of defining threads-by
implementing the Runnable interface.
The Runnable interface is simple. It consists of a single method, run () :
To start a new thread defined in this way, we must first create an instance of
MyThread. We use the MyThread instance to create an instance of Thread and then
invoke the start() method:
PURPOSE
This example demonstrates thread creation by implementation of the Runnable
interface.
DESCRIPTION
This class defines a thread that behaves the same as the one in Example 11 .1.
11.1 Th~ads • 553
SOLUTION
count+= inc;
Thread .sleep (delay);
}
} catch (InterruptedException e) {
e .printStackTrace() ;
}
}
Programmers have only high-level control of thread execution by controlling the states
of the life cycle of threads. The execution of thread i ultimately controlled by the
Java virtual machine. Programmer may also influence the execution of threads by
manipulating the priorities of threads.
554 • Concurrent Programming
Figure 11.2
Alive yield()
The life cycle of Blocked
threads. wait()
Wait to be Not
notified interrupted
notify() I
notifyAll()
join()
Wait for target interrupt 0
to finish
Target finish
Dead
sleep()
Sleeping Interrupted
run() returns
Time out
interrupt()
/throws InterruptedException
11.1 Threads • 555
Method Description
runnable) and the higher priority thread will start running. A thread that is currently
running relinquishes control when one of the following occurs:
Safety properties are conditions that should hold throughout the lifetime of a program.
They stipulate that nothing bad should ever happen. An important safety property is
the consistency of object states. While an object is being modified, it may go through
a series of intermediate states that are inconsistent or invalid. If the thread that is
modifying the object is interrupted, it may leave the object in an inconsistent state.
When another thread tries to access an object in an inconsistent state, this action
may lead to incorrect, perhaps disastrous, behavior. Let us consider an example of
a simplified bank account class. The withdraw () method attempts to withdraw a
specified amount of money from an account. It succeeds when the withdrawal amount
is less than or equal to the balance in the account.
11.2.1 Synchronization
In this case, the entire body of the method is the critical region. A synchronized
statement takes the following form:
synchronized(exp) {
(do something)
}
where the expression exp must be of reference type. The statements enclosed in the
synchronized block comprise the critical region.
The synchronization mechanism is implemented by associating each object with
a lock. A thread must obtain exclusive possession of the appropriate lock before
entering the critical region.
■ For a synchronized instance method, the lock associated with the receiving object
this is used.
■ For a synchronized statement, the lock associated with the result of the expression
exp is used.
The lock is released when the thread leaves the critical region. The lock may also be
released temporarily before leaving the critical region, when the wait () method is
invoked (see Section 11.2.2 [p. 564]).
The synchronized method in the following code fragment:
class HyClass {
synchronized void aMethod() {
(do something)
}
As only one thread can have exclusive possession of the lock of a critical region,
only one thread at a time can execute the statements in the critical region. Moreover,
different critical regions may share the same lock. For example, all the synchronized
instance methods use the lock associated with the receiving object. Consider the
following example:
public class A {
synchronized void ml O { . . . }
synchronized void m2 0 { . . . }
void m3 () { . . . } // unsynchronized
}
11.2 Thread Safety and Liveness • 559
PURPOSE
This example shows a simple bounded queue implementation that is not thread-safe.
Therefore, it is only suitable for sequential, that is, single-threaded, applications.
DESCRIPTION
A bounded queue is a first-in, first-out queue with a fixed capacity.
SOLUTION
The following program for the BoundedQueue class is implemented with a circular
array. The capacity of the queue is specified by the argument to the constructor. The
capacity of the queue may not be changed. The methods of the BoundedQueue clas
are summarized in the following table:
Method Description
uentlal version)
public class BoundedQueue {
protected Object rep[];
protected int front= O;
protected int back= -1;
protected int size= 0;
protected int count= O;
560 • Concurrent Programming
This implementation of the bounded queue has two shortcomings. The put ()
method ignores the new element when the queue is full, and the get () method returns
~ull when the queue is empty. When used with sequential programs, however, this
unplementation is reasonable.
The main () method carries out a simple test of the BoundedQueue class, giving
the following output:
put: 0
put : 1
put: 2
put: 3
put: 4
put: 5
put : 6
put : 7
put: 8
put : 9
get: 0
get : 1
get : 2
get : 3
get: 4
get: 5
get : 6
get : 7
get: 8
get : 9 ■
The result in Example 11.4 is exactly what we expected. However, the sequential
version of the BoundedQueue class is not thread-safe. An easy way to make a class
thread-safe is to synchronize all the methods of the class.
PURPOSE
This example demonstrates the use of synchronization to ensure thread afety.
SOLUTION
The SyncBoundedQueue clas is identical to the the BoundedQueue in Exam-
ple 11.4, except that it i fully synchronized and thread- afe.
•
~
• '
.
I • ~
'
.
t' ' •
. .
Figure 11.3
Thread
The producer,
consumer, and
bounded queue. Producer Consumer
SyncBoundedQueue
i ~ -~ -=:-------------
11.2 Thread Safety and Uvcncss • 563
produce: 0
consume : 0
produce: 1
produce : 2
produce: 3
produce : 4
produce : 5
consume : 1
produce : 6
produce : 7
consume: 2
produce : 8
produce : 9
produce : 10
produce : 11
consume: 3
produce : 12
produce: 13
produce : 14
consume: 4
consume: 5
consume: 6
consume : 8
consume: 12
Note that the producer produces items faster than the consumer consumes the
items. This implementation of the bounded queue causes some of the items to be lost.
Synchronization ensures the mutual exclusion of two or more threads in the critical
regions, but it does not address cooperation among threads. The SyncBoundedQueue
in the preceding example is thread-safe. However, the producer and the consumer
threads are not cooperating very well, and this lack of cooperation leads to a loss of
items in the queue. The producer and the consumer should cooperate in the following
ways:
• When the producer attempts to put a new item into the queue while the queue is
full, it should wait for the consumer to consume some of the items in the queue,
making room for the new item.
• When the consumer attempts to retrieve an item from the queue while the queue
is empty, it should wait for the producer to produce items and put them in the
queue.
TAILI 11-,I
Thread-Controlling Methods
Method Description
PURPOSE
Thi example demon trate the u e of the wait () and notify () methods to imple-
ment guarded uspen ion and en ure cooperation among threads.
566 ■ Concurrent Programming
DESCRIPTION
This version of bounded queue supports the cooperation between the producer and
the consumer in the way described previously.
SOLUTION
The BoundedQueueWithGuard class extends the BoundedQueue class. It is fully
synchronized. The put () and get () methods are overridden to support cooperation
between the producer and the consumer.
~ :--r-rn,·~~ ,-~:~-. '~';"~Y'"-~~";,., ..·.
"-:a............-.:..... . . , . . . . ~ \ , , , , , . _ ~ ~ ,...,...,.....~ f.,
: ·: ·,·:
~
.. ....·~- .l
The guard of the put O method ensures that the queue is notfull, and the guard of
the get () method ensures that the queue is not empty. The put () method invokes the
wait O method to suspend the producer thread temporarily when the queue is full.
Similarly, the get O method invokes the wait () method to suspend the consumer
thread temporarily when the queue is empty. Both methods invoke the notify 0
method at the end to wake up a suspended thread, if any.
, , - . .
'
0 I • ' • •
' '
- ••
. ••
.
synchronized Object get() {
try {
while (isEmpty()) {
wait();
}
} catch (Interrupted.Exception e) {
e . printStackTrace();
}
Object result= super.get();
notify();
return result;
}
The interaction between the put () and get () methods, which are invoked by
the producer and consumer threads, respectively, is best illustrated by two scenarios:
Scenario A:
The producer produces items faster than the consumer consumes them.
1. The producer thread acquires the lock associated with the queue and invokes the
put() method, but the queue is full.
2. The wait () method is invoked, and the producer thread is suspended. The lock
associated with the queue is temporarily released by the producer thread.
3. The consumer thread acquires the lock associated with the queue and invokes the
get () method. The queue must not be empty; indeed, it is full. So the guard of
the get () method returns true, and one item is removed from the queue.
4. The notify () method is invoked to awaken the suspended producer thread. The
consumer thread completes the invocation of the get () method and releases the
lock associated with the queue.
5. The producer thread is awakened in the put () method and reacquires the lock
associated with the queue. The guard of the put () method is true this time, and
execution resumes where it left off.
Scenario B:
The producer produces items more slowly than the consumer consumes them.
1. Toe consumer thread acquire the lock associated with the queue and invokes the
get () method when the queue i empty.
2. The wait () method is invoked, and the consumer thread is suspended. The lock
associated with the queue is temporarily released by the consumer thread.
3. The producer thread acquire the lock a~sociate~ ~th the queue and invokes the
ut () method. The queue is not full; mdeed, 1t 1s empty. So the guard of the
:ut () method returns true, and one item is inserted into the queue.
568 • Concurrent Progranvning
The main() method performs a simple test of the bounded queue, with the
producer and the consumer running on separate threads.
produce : 0
consume: 0
produce : 1
produce: 2
consume: 1
produce: 3
consume: 2
produce: 4
produce: 5
consume: 3
produce : 6
produce : 7
produce: 8
consume : 4
produce: 9
consume: 5
produce: 10
consume : 6
produce: 11
consume: 7
produce : 12
consume: 8
produce: 13
consume: 9
produce: 14
consume: 10
consume: 11
consume: 12
consume: 13
consume: 14
•
11.2 Thread Safety and Liveness • 569
11 ·2 ·3 Liveness Failures
liveness refers to desirable conditions that will come about during the lifetime of
a program. In other words, liveness properties stipulate that something positive will
eventually happen. For example, common liveness properties include the following:
(I) A certain task will be completed eventually; (2) a thread should always respond
to user input until the thread is terminated; and (3) the status of certain systems must
be displayed and updated constantly.
Safety properties can be ensured locally, but liveness properties are context
dependent. Ensuring liveness properties is a much more difficult task than ensuring
safety properties. Some common types of liveness failures are contention, dormancy,
deadlock, and premature termination.
Contention
Contention (also known as starvation or indefinite postponement) occurs when a
runnable thread never gets a chance to run. This can occur when there are always
one or more runnable threads with higher priorities or when there is a runnable thread
with the same priority but it never yields.
Let us consider the run () method of a typical animation applet:
public class AnAnimation {
protected Thread animationThread;
public void run() {
while (Thread.currentThread() == animationThread) {
repaint ();
try {
Thread . currentThread(). sleep (delay);
} catch (InterruptedException e){
e.printStackTrace( );
}
}
}
II other fields and methods . . .
}
The repaint() method draws the current frame on a separate thread having the
same priority as the animation thread. If the sleep O method were not invoked, the
repaint thread would never get a chance to run and the animation would appear as a
blank area.
To avoid contention, the thread with the highest priority must periodically invoke
the sleep() or yield O method to provide other cooperating threads having the
ame or lower prioritie with a chance to run.
Dormancy
Dormancy occurs when a thread that i blocked never becomes runnable. A com-
mon cause of dormancy is that a thread blocked by an invocation of the wait()
method i never awakened by notify O or notify All() . For example, in the
5 70 • Concurrent Programming
t. The queue becomes empty. The consumer thread invokes the wait () method
inside the get () method and becomes blocked.
2. Toe producer thread puts items into the queue, but the consumer thread is not
awakened because the invocation of the notify() method is omitted. The
consumer thread remains blocked.
3. The queue becomes full. The producer thread invokes the wait () method inside
the put () method and becomes blocked.
At this point, both the consumer and producer threads are blocked. If there are no
other threads to awaken them, they become dormant.
To avoid dormancy caused by waiting, be sure that each thread that can be blocked
by the wait () method will be awakened by another thread that invokes the notify ()
or notifyAll() method. When in doubt, use the notify All() method, which
awakens all the waiting threads and makes the system less dormancy-prone.
Deadlock
Deadlock occurs when two or more threads block each other and none can make
progress. It is usually caused by two or more threads competing for multiple shared
resources and each thread requiring exclusive possession of those resources simul-
taneously. Consider the following oversimplified example. The DiskDri ve class
represents a disk drive, and the copy () method copies a file from this disk drive
to another drive.
To maintain the integrity of the file being copied, all the methods are synchro-
nized. The copy () method needs to acquire the locks associated with both the source
and destination disk drives. Let us assume that we have two disk drives, c and d . On
11.3 Design CaK Study-Tic-Tac-Toe Game • 571
Neither thread is able to obtain the lock needed to proceed, so they are in a
"deadlock."
In general, detecting and preventing deadlock is difficult. An extensive body of
work on deadlock detection and prevention is available. A more detailed discussion
on this topic is presented in Lea [2000].
Premature Termination
Premature termination occurs when a thread is terminated before it should be, im-
peding the progress of other threads. For example, in the BoundedQueueWithGuard
class [p. 566], premature termination of either the producer or the consumer th.read
causes the other thread to be blocked forever (that is, to become dormant).
import java.awt.•;
import java.awt.event.•;
public class Board extends Canvas {
protected Game game;
protected int row, col;
protected int rowHeight, colWidth;
protected int board O [] ;
protected int maxHoves;
protected int moves ;
protected boolean over= false;
protected int winner= O;
(Constructor Board() on page 572}
(Accessors of fields on page 573}
(Method paint() on page 574}
(Method recordMove () on page 575}
(Method isLegalMove () on page 575}
(Method checkGame ( ) on page 576}
(Auxiliary methods checkRowO, checkColO , and checkDiagonalO on page 576}
(Inner class MouseHandler on page 577}
}
The fields of the Board class are described in Table 11 .3. The constructor of the
Board class simply initializes the fields.
moves • O;
board• new int[row] [col];
addMouseListener(new MouseHandler());
}
The following accessors of the Board class return the values of the corresponding
fields:
Methods Description
game The Game object with which this game board is associated
row, col The number of rows and columns on the game board
rowHeight The height of each row
col Width The width of each column
board The two-dimensional array that records the state of the game
board, each cell containing the ID of the player who occupies
the cell and the player ID starting at 1
moves The number of moves that have been made
maxMoves The maximum number of move allowed in the game
over The boolean flag that is set to true if the game i over
wi nner The ID of the player who won the game, which is O while the
game is in progre
574 • Concurrent Programming
Figure 11.5
The tic-tac-toe
game board.
X
0 X
0
Player 1's tum
Applet started.
The paint () method paints the game board. A screen shot of the game board is
shown in Figure 11.5. The paint () method first draws the grid. It then draws an X
for each cell occupied by player 1 and an O for each cell occupied by player 2.
~- ~
\,. ,
~
I
: -~~-~-~r--:~~~7 .-r ,· ):~;-----~:1~'!";):r~~~;.v-.f");
./ .. • -• .,-~-- .,--,•~-~-r ..,.,. ~ · - ..... - } •~..,...., - : '
case 2:
g.setColor(Color.blue);
g.drawString("O", x, y);
break;
}
}
}
}
}
The isLegalMove O method returns true if the specified move is legal. A move
is legal if the cell is unoccupied.
The checkGame () method checks to determine whether the game is over. The
game is over if all the cell on the game board are occupied or one of the players
has won. It depends on three auxiliary method - checkRow O, checkCol (), and
checkDiagonal ()- to detennine whether one of the players ha won.
5 76 • Concurrent Programming
return true;
}
The inner class MouseHandler receives mouse clicks on the game board, which
indicate moves made by a human player.
The Game class extends the Applet cla . It contain a game board and a me age
bar. The field player is an array of players in the game. In thi implementation the
number of players is fixed at 2. The field turn refer to the player ~ ho currently has
the turn (that is, the player who i upposed to make the ne, t move).
Clailhae
import java .awt.•;
import java. applet .Applet;
public class Game extends Applet {
protected Board board;
protected Label messageBar ;
578 • Concurrent Programming
The constructor initializes the fields by creating the board and the message bar.
setLayout(new BorderLayout());
add(board, BorderLayout.CENTER);
add(messageBar, BorderLayout .SOUTH);
}
The getPlayer () method returns the player who has the tum. The get Board ()
method returns the game board. The is Over () method returns true if the game is
over. The displayMessage () method displays a message on the message bar.
if (is0verO) {
int winner• board.getWinner();
if (winner>= 1) {
displayHessage("Player" +winner+" won . ");
} else {
displayHessage("It's a draw");
}
for (int n • 0; n < players.length; n++) {
players[n].stop();
}
}
return true;
} else {
return false;
}
}
The Player class is an abstract class that represents players in the game. Different
types of players are represented by different subclasses of this class. The field game
refers to the game the players are in. The field id is the ID of the first player. The
field next refers to the player whose tum follows that of the preceding player. In two-
player games, next refers to the second player. The field turn refers to the player who
currently has the turn. It is this player's turn if turn = this. The turn and next
fields of the player objects are initialized in the init () method of the Game class.
The getld() method returns the ID of this player. The setNext O method
sets which player is after this player. The has Turn () method is invoked when this
player should have the tum. This method is invoked by the preceding player, and it
awakens this player (see the run () method). The makeMove () method is a hook
method for the subclasses to override. This method should return the next move to be
made by the player. The selectCell () method is invoked by the MouseHandler
of the Board class. It informs the player that a mouse click has been received and
a cell position given. This method is ignored by the MachinePlayer class, but it is
used by the HwnanPlayer class to get the move made by a human.
The run () method is the body of the player thread and is also a template method.
First, it waits until this player gets the tum. Then it invokes the hook method
makeMove () to make a move.
The HwnanPlayer class implements the makeMove () method. It simply waits
for a mouse click on the game board.
c..
public class Machi nePlayer extends Player {
protected int nCells ;
publ ic MachinePlayer(Game game , i nt i d) {
super (game , id);
Board board= game.getBoard ();
nCells = board.getRow() * board .getCol ();
}
The init () method of the Game class initializes the players according to the
parameter type.
The Player class implements a behavior that allows the players taking turns to make
moves. This behavior can be generalized by the following idiom that extends to more
than two objects, or players, and is applicable to nongame applications:
If there are n participants and they take turns in the order, Po, p 1, •. . , p,._ 1, they
must be initialized as
Po-next == Pt
Pt .next = P2
Pn - 2 ·next == Pn- 1
Pn- 1· next == Po
To start the process, we do the following:
Po . start() ;
Pt. start O;
Pn - t. start() ;
Po. has Turn O ;
Using the taking-turns idiom, we can extend the two-player game shown here to
multiplayer games. This is left as a project (Project l l.1 ).
f FURTHER READING
[ EXERCISES
11.1 Complete the Account class on page 556 getBalance () . All critical regions of the clas
and be sure that il is thread-safe. ll should should be protected with synchronization as
contain methods such as deposit O and needed.
Projects ■ 585
ll.l Implement a thread-safe linked list class based emergency vehicles occasionally, in different
~ the_~inkedlist class in Chapters 6 and 7. directions. Develop a GUI program to animate
Cntical regions of the class should be traffic lights and traffic flows. (Note: Take
protected with synchronization as needed. precautions so that your program does not
ll.J Using the BoundedQueueWithGuard class as deadlock.) Bonus: Add a left-tum signal to
a model, implement a thread-safe Stack class each set of traffic lights.
with guarded suspension. The Stack class 11.3 Develop a Java program that simulates an
should contain the methods pop (), push () , elevator control system for a bank ofm elevators
isFull O, and isEmpty () . Test the Stack of an n-story building. Each floor, except the
class by using classes similar to the Producer top and bottom, has an Up and a Down button.
and Consumer classes presented in this chapter. The top floor has only the Down button. and
the bottom floor has only the Up button. Inside
each elevator is a button for each floor. The goal
of the system is to ensure that
(a) one of the elevators moving upward will
stop at the floor for which the Up button is
11.1 Design and implement a multiplayer tic-tac-toe pushed.
game. (Hint: Use a larger board and design a (b) one of the elevators moving downward will
new scoring system.) stop at the floor for which the Down button
11.2 Develop a Java program that simulates a traffic is pushed.
control system for a four-way intersection. The (c) each elevator must stop at the ith floor if
system controls four sets of traffic lights, facing the button i in this elevator is pushed.
north, east, south, and west. Each set of lights
Develop a GUI program to animate the status
can be in one of the following states: green,
of all the buttons and the movement of the
yellow, and red. The goals of the traffic control
elevators. Two testing modes of the system
system are
should be supported:
• never to give green lights simultaneously
(a) Use independent threads to simulate the
to traffic flows that may collide.
requests of the passengers.
• to avoid unnecessary blocking of the traffic
flows. (b) Manually push the buttons on each floor
• to give green lights to emergency vehicles. and inside each elevator.
CHAPTER OVERVIEW
In this chapter, we first discuss two pure Java mechanisms for distributed computing:
socket-based communication and remote method invocation (RMI). We illustrate the
design and implementation of distributed applications, using a stock ticker application.
We then discuss a Java mechanism, JDBC, for communicating w ith relational databases.
We also briefly discuss a mechanism for interfacing Java applications with non-Java
applications in distributed computing environments: the Common Object Request Broker
Architecture (CORSA). This chapter serves as an introduction to developing distributed
applications in Java.
587
•
588 • Distributed Computing
SOCKET-BASED COMMUNICATION
Sockets are the end points of logical connections between hosts and can be used to
send and receive data. At the application programming level, it is assumed that the
socket connections are reliable; that is, the data sent from one end of the connection
will be received at the other end in the same order and with no loss . Sockets are sup-
ported by most programming languages and platforms in use today. Java applications
can use sockets to communicate with applications written in other languages.
There are two kinds of sockets: server sockets and client sockets. A server socket
waits for requests for connections from clients. A client socket can be used to send
and receive data.
Server Sockets
Each server socket listens at a specific port . The port number is necessary to distin-
guish different servers running on the same host, so each server must have a unique
port number.
A server socket must be running on the server host before its clients initiate con-
tact. After the server socket is contacted by a client, a connection can be established,
and a client socket is created for the application running on the server host to com -
municate with the client that initiated the contact.
LIIL1
12.1 Socket-Based Communication • 589
ServerSocket(int port)
ServerSocket(int port, int backlog)
The parameter port specifies the port number at which the server socket will
be listening to requests from clients. When multiple clients are contacting the same
server socket at the same time, they will be put in a waiting queue and will be processed
in the order in which they are received. The optional parameter backlog specifies
the maximum length of the waiting queue. Server sockets can be created only with
Java apps, not applets. The commonly used methods of the ServerSocket class are
summarized in the following table:
Method Description
accept O Waits for a connection request. The thread that executes the
method will be blocked until a request is received, at which time
the method returns a client socket.
close O Stops waiting for requests from clients.
try {
ServerSocket s = new ServerSocket (port);
while (true) {
Socket incoming = s . accept () ; II obtain a client socket
(Handle a client}
}
} catch (I □Exception e) {
(Handle exception: fail to create a erver ocket}
}
Client Sockets
A client socket i an instance of the Socket cla and can be obtained in two way :
1. On the client side, client ocket can be created by u ing the con tructor
Socket (String host, int port)
The parameter host pecifie the addre of the ho t, and the parameter port
specifie the port number. Client ocket, can be created and u ed by both Java
app and applet .
2. On the erver ide, client ocket are returned by the accept O method of the
ServerSocket cla , after reque t for onnection have been received from
lient .
590 • Distributed Computing
Communication between the server and the client is handled by client sockets
at both ends. Each client socket has an InputStream object for receiving data and
an OutputStream object for sending data. When the InputStream and Output-
Stream objects are used, sending and receiving data between a client and a server
are essentially no different from reading data from and writing data to files . The com-
monly used methods of the Socket class are summarized in the following table:
Method Description
The following program segment illustrates the typical use of client sockets for
sending and receiving text data:
try {
Socket s = new Socket (host, port) ; II create a client socket
PrintWriter out= new PrintWriter(
new OutputStreamWriter(s . get □utputStream()));
BufferedReader in= new BufferedReader(
new InputStreamReader(s.getlnputStream()));
{Send and receive data)
in . close() ;
out . close O ;
s . close() ;
} catch ( ! □Exception e) {
(Handle exception: connection fails)
}
PURPOSE
This example illustrates the basic elements of a server application.
DESCRIPTION
Thfa server echoes each line of text that it receives from its client. The client can
terminate the connection by sending a line that says "BYE."
12.1 Socket-Based Communication • 591
SOLUTION
Typically, a server is supposed to run for a long time. So a server usually contains an
infinite loop of some kind. In this case, each iteration of the loop handles one client.
Thus this server can handle multiple clients sequentially (that is, only one client at a
time), not simultaneously.
import java.io.•;
import java.net.•;
public class EchoServer {
public static void main(StringO args) {
try {
ServerSocket s a new ServerSocket(8008);
while (true) {
Socket incoming= s . accept();
BufferedReader in
= new BufferedReader(new InputStreamReader(
incoming.getinputStream()));
PrintWriter out
= new PrintWriter(new OutputStreamWriter (
incoming.get0utputStream()));
out.println("Hello! This is the Java EchoServer.");
out .println("Enter BYE to exit . ");
out . flush();
while (true) {
String str = in . readLine() ;
if (str =-- null) {
break ; // client closed connection
} else {
out .println("Echo : 11 + str);
out. flush() ;
if (str.trimO . equals ( "BYE")) {
break;
}
}
}
incoming . close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
The echo server will listen on port 8008. The method invocation in. readLine ()
attempt to receive a line of text from the client. If the connection i alive but the client
doe not send anything, the method invocation will be blocked. If the client sends a
line of text, the method invocation will return with that line of text. If the client closes
592 • Distributed Computing
the connection, the method invocation will return with null. The flush() call on
the output stream forces the messages to be sent to the client right away.
The server application can be compiled as usual. Let us say that we compile and
start the echo server on a host called saturn:
saturn¼ java EchoServer
Now, the server is ready and is waiting for clients. We can test the echo server
with the telnet program, which is available on almost all platforms. Let us say that
we run telnet on a host called venus:1
venus¼ telnet saturn 8008
Trying 140.192.34.63 .. .
Connected to saturn .
Escape character is •-]' .
Hello! This is the Java EchoServer.
Enter BYE to exit .
Hi, this is from venus
Echo: Hi, this is from venus
BYE
Echo : BYE
Connection closed by foreign host.
We can also write a Java client that communicates with the echo server:
PURPOSE
This example illustrates the basic elements of a client application.
DESCRIPTION
This client contacts the echo server, sends 10 lines of text to the echo server, and prints
out the data it receives from the server.
SOLUTION
A
import java. io.•;
import java. net.•;
public class EchoClient {
public static void main(String[] args) {
try {
String host;
I. Although here we run the server and the client on difTcrent hosts, it should be okay to test the server
and client on the same host. 1n this case, the host name is localhost or 127 . o.o. 1.
12.1 Socket-Based Communication • 593
if (args.length > 0) {
host• args[0];
} else {
host= 11 localhost 11 ;
}
Socket socket= new Socket(host, 8008);
BufferedReader in
= new BufferedReader(new InputStreamReader(
socket . getinputStream O)) ;
PrintWriter out
= new PrintWriter(new 0utputStreamWriter(
socket.getOutputStream( )));
// send data to the server
for (inti= 1; i <= 10; i++) {
System . out.println( 11 Sending: line 11
+ i);
out.println( 11 line 11 + i);
out. flush() ;
}
out . println ( 11 BYE 11 ) ;
out.flush();
II receive data from the server
while (true) {
String str = in . readLine();
if (str = null) {
break;
} else {
System.out.println (str) ;
}
}
} catch (Exception e) {
e.printStackTrace() ;
}
}
}
The first argument of the program is the name of the host on which the erver
is running. If the host name is absent, localhost is assumed. Thi client contacts
the server on the given host at port 8008. The method invocation in. readLine ()
behaves the same on the client side as on the erver ide. To te t this client, we first
need to run the echo server on the host saturn: saturn% j ava EchoServer. Then
we compile and run EchoClient on the host called venus. The output i
venus¾ java EchoClient saturn
Sending: line 1
Sending: line 10
Hello! This is Java EchoServer .
Enter BYE to exit .
Echo : line 1
Echo : line 10
Echo : BYE
59 4 • Distributed Computing
This echo server handles one client at a time. This behavior is not acceptable
in some situations. It is more desirable for the server to be able to serve multiple
clients simultaneously. To accomplish that purpose, each client should be handled b y
a separate thread.
PURPOSE
This example illustrates a server that handles multiple clients simultaneously with the
use of threads.
DESCRIPTION
The behavior of this echo server is similar to that of the simple echo server; the
difference is that each client is handled by a separate thread.
SOLUTION
The ClientHandler class defines the threads that handle the clients. Note that the
body of the run () method is essentially the same as the body of the loop of the echo
server.
incoming.getOutputStream()));
out .println ("Hello! This is the Java MultiEcboServer.");
out.println("Enter BYE to exit.");
out. flush() ;
while (true) {
String str = in.readLine();
if (str •• null) {
break;
} else {
out . println( "Echo : "+ str) ;
12.1 Sock~-BaKd Communication • 595
out.flush();
if (str.trimO .equals (" BYE" ))
break ;
}
}
incoming . close();
} catch (Exception e) {
e . printStackTrace();
}
}
}
The Mul tiEchoServer class is the server, which creates a server socket. When-
ever a cUent request is received, a new ClientHand1er object, which is a thread, is
created and started.
So far, we have developed two erver and a client that are all Ja a app . Although
only a Java app can be a erver in ocket-ba ed client- erver application . a client can
be written as either an app or an applet. Example L.4 how a client that i written
as an applet.
PURPOSE
Thi example illu trates a client implemented a an applet.
DESCRIPTION
This applet , how, the number of vi its, or hits, of the Web page that contains the
applet. A creen shot of the applet i hown in Figure 12. l .
596 ■ Distributed Computing
Figure 12.1
I Applet
The visitor counter
applet. 'You are visitor: 17
I
Applet started.
SOLUTION
The CounterServer class is the visitor counter server. It must be running on the
host where the Web server (that is, the HTIP server) is running.
import java.io .• ;
import java. net . •;
public class CounterServer {
public static void main(String[] args) {
System . out . println("CounterServer started . ");
int i = 1;
try {
// read count from the file
InputStream fin= new FileinputStream( "Counter . dat");
DatainputStream din= new DatainputStream(fin);
i = din . readlnt() + 1;
din.close();
} catch (!□Exception e) {
e.printStackTrace();
}
try {
ServerSocket s = new ServerSocket(8190);
while (true) {
Socket incoming= s.accept();
DataOutputStream out
= new DataOutputStream(incoming . getOutputStream()) ;
System. out . println( "Count: " + i) ;
out.writeint(i) ;
incoming . close();
OutputStream fout .. new FileOutputStream("Counter.dat") ;
DataOutputStream dout • new DataOutputStream(fout);
dout . writeint(i) ;
dout . close() ;
out . close() ;
i++;
}
} catch (Exception e ) {
e . pri ntStackTrace() ;
}
12.1 Socket-BaHd Communication • 597
When the server starts up, it attempts to open a data file named Counter. dat to
retrieve the last count. If the file Counter. dat does not exist, an !□Exception will
be thrown. The exception will be caught and the count set to I , the initial value of i.
The current count is saved to the file every time the count changes so that the count
can be restored and continue after the server is shut down and restarted. The data file
is written with a DataOutputStream object, so it is in binary form. Similarly, the
count is sent to the client in binary form.
The Counter class is the visitor counter applet. The applet communicates with
the server to retrieve the count. In the ini t () method, a client socket is created.
Recall that an applet can communicate only with a server that resides on the same
host as the Web server. The host name of the client socket can only be the host of the
document base.
The servers in all the examples so far deal with each client independently from
other clients. No client-to-client communication is supported. The following example
illustrates how clients can communicate with each other via the server.
PURPOSE
This example illustrates how clients can interact with one another through the server.
DESCRIPTION
The behavior of this echo server is similar to that of the echo server in Example 12.4;
the difference is that this server will broadcast messages received from a client to all
other active clients. This capability allows its clients to "chat" online.
SOLUTION
The BroadcastClientHandler class defines a thread that handles the clients. The
BroadcastEchoServer class is the server. It contains a set of active clients, which
will be used while the server is broadcasting messages.
Tbt ..........;.s __
public class BroadcastEchoServer {
static protected Set activeClients = new HashSetO;
public static void main(String[] args) {
inti= 1;
try {
ServerSocket s = new ServerSocket(8010);
while (true) {
Socket incoming= s.accept();
BroadcastClientHandler newClient =
new BroadcastClientHandler(incoming, i++);
activeClients.add(newClient);
newClient.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
is also broadcast to all other active clients. Thus the sendMessage O method needs
to be synchronized because it will be invoked by all the threads that are handling
clients.
To test this broadcast server, we first start the server on a host called neptune:
neptunei. java BroadcastEchoServer. Then we start two telnet clients on
hosts venus and saturn, respectively:
In this section, we develop a stock quote server and stock ticker clie nt apple t. The
stock quote server maintains the current quotes of the stocks of various companie .
A random number generator is used to simulate the fluctuation of the quotes ( ee
Example l 1.2 [p. 551 ]). The stock ticker client applet displays the quotes of a elected
list of companies that are of interest. The ticker symbols and quotes scroll acros the
12.1 Socket-Based Communication • 601
Figure 12.2
lcilJJ•j$JM4W§i@I-J8J xf
The stock ticker Applel
client applet. 8 Sun 103 Int
jApplet started.
ticker banner continuously. A screen shot of the stock ticker client applet is shown in
Figure 12.2.
This application involves real-time updates of stock quotes. There are generally
two strategies for handling real-time updates:
Client pull: The client periodically contacts the server to receive the current
quotes from the server.
Server push: The server notifies clients whenever the quote of one of the tocks
watched by the clients has changed.
The strategy chosen depends on many factors, including acceptable latency of up-
dates, frequency of changes, and the like. We implement the tock ticker application
by using both strategies.
name1 quote 1
name2 quote2
The structure of the de ign is shown in Figure 1__ 3_ The three haded cla e
are the new classes to be implemented here. The Ticker clas handle the di play
and animation of the quote . The di play and animation portion of the client i
implemented similarly to the ScrollingBanner applet in Example -.3 [p. 194].
----L---,* 1 ...---..._---
TlckerPullCllent - - - - -Server
L - - - - - - - ' Client
- -.___
QuotePullSeNer
______,
602 • Distributed Computing
The update strategy of the stock quote is separate from the display and animation of
the stock quotes and is implemented in the class TickerPullClient . The separation
allows the Ticker class to be reused in different implementations of the stock ticker
client with different update strategies and communication mechanisms.
Field Description
vatch Watch list-a string that consists of a list of the names of the
companies to be watched by this client
symbol Array of names of the companies in the watch list
quote Array of current quotes of the corresponding companies in the
symbol array
prevQuotes String containing the quotes of the previous cycle
curQuotes String containing the quotes of the current cycle
url URL of the quote server
The methods of the Ticker class are summarized in the following table:
Method Description
import
import
java. awt.*;
java.util.•;
.......
,,
import java. net.•;
import java.io.•;
public class Ticker extends DBAnimationApplet {
protected Font font= new Font("Sans-serif", Font.BOLD, 24);
protected int offset= 1;
protected int x, y;
protected String watch;
protected String symbol O , quote[] ;
protected String prevQuotes, curQuotes;
protected URL url;
(The ini tAnimator () method on page 603)
(The paintFrame () method on page 604)
(The ini tQuotes () and updateQuotes () methods on page 604)
}
The ini tAnimator () method initializes the fields of the ticker client. The watch
list is obtained from the applet parameter watch. The watch list is broken down into
single names and then stored in the array symbol.
\ Viewing area
(x, y)
The methods ini tQuotes () and updateQuotes () are hook methods that can
be overridden in the subclasses. A null implementation is provided for ini tQuot es () .
The implementation of updateQuotes O simply forms the curQuotes string by
concatenating all the symbols and quotes.
----=::,---
_..
12.1 Socket-~d Communication ■ 605
namen quoten
The client picks out the companies on its watch list and updates the quote array.
Then a new curQuotes string is formed, using the new quotes.
dlint
import java.awt . *;
import java.util.* ;
import java.net . *;
import java.io.*;
public class TickerPullClient extends Ticker {
protected void updateQuotes() {
int i ;
try {
Socket t = new Socket (url.getHost () , 8001 );
BufferedReader in=
new BufferedReader (new InputStreamReader (t. getlnputStream ()));
String line ;
while ((line= in.readLine ()) != null) {
StringTokenizer tk = new StringTokenizer (line);
String name= tk . nextToken();
for (i = O; i < quote .length ; i++) {
if (symbol[i] .equals (name)) {
String newQuote = tk .nextToken();
quote[i] = newQuote ;
}
}
}
t. close ();
} catch (I0Exception e ) {
e . printStackTrace ();
}
super. updateQuotes O ; // form the curOuotes string
}
}
This completes the implementation of the stock ticker client with the client pull
strategy.
changes are simulated with a random number generator. The array symbol contains
the names of all the companies, and the array quote contains the current quotes of
the corresponding companies in the array symbol.
import java.io.• ;
import java .net.•;
public class QuotePullServer extends Thread {
static protected String symbol[] =
{ "IBM", "Sun", "Intel", "Apple" };
static protected int quote[] =
{ 100, 100, 100, 100 };
{The main() method on page 606)
(The run() method on page 606)
}
The main () method listens to incoming ticker clients. It sends the quotes of all
companies to any client that contacts the quote server.
The run () method simulates the changes of the quotes with a random number
generator and updates the quote array.
This completes the stock quote server implementation using the client pull
strategy.
name 1 quote 1
namei quotei
namen quoten
DONE
■ The connection between the ticker client and the quote server i maintained until
the client decides to close it.
■ Whenever the quote of a company is changed, the quote erver end the quote
to those clients who are watching the company. in the format
name quote
The structure of the design is shown in Figure 12.5. The haded boxe contain
the new classes to be implemented here.
1. The TickerPushClient cla extend the Ticker cla , . It make. the initial
contact with the quote erver, retrieve the initial quote . and handle the di play
of the quote .
2. The QuoteListener class i a thread that Ii ten~ to the quote erver for updates
on the quote. Each client ha a QuoteListener object that maintain a connec-
tion with the quote server and update the quote displayed by the ticker client
when change. occur.
608 ■ Distributed Computing
* 1
TickerPushCllent QuotePushServer
Client Server
1 1
1 *
1 1
QuoteUstener lickerClientHandler
Client Server
a.. Qu~.Unaer
class QuoteListener extends Thread {
public QuoteListener(TickerPushClient ticker, BufferedReader in) {
this.ticker= ticker;
this.in= in;
}
in . close();
} catch (! □Exception e) {
e.printStackTrace();
}
}
This completes the implementation of the stock ticker client using the server push
strategy.
~iEiiU
import java.io .•;
import java . util.•;
import java.net.•;
public class QuotePushServer extends Thread {
static protected String symbol[] =
{ "IBM", "Sun", "Intel", "Apple"};
static protected int quote[] =
{ 100, 100, 100, 100 };
static protected Set clients s new HashSet();
(The main () method on page 610)
(The run () melhod on page 611 )
(The get Quote() method on page 612)
}
The main() method listens to incoming ticker clients, and creates a Ticker-
ClientHandler object, which is a thread, for each ticker client, adds the client
handler to the active clients set clients, and starts the thread.
while (true) {
Socket incoming• s . accept();
TickerClientHandler newClient • new TickerClientHandler(incoming) ;
clients.add(newClient);
newClient.start();
}
} catch (Exception e) {
e.printStackTrace() ;
}
}
The run () method simulates the changes of the quotes with a random number
generator and updates the quote array. Furthermore, if the quote of a stock is
changed, it iterates through the set of handlers of active ticker clients and invokes
the newQuote () method of the TickerClientHandler class to request that the
handlers push the change to their respective clients.
The get Quote() method retrieve the current quote of a company by its name.
It i u ed by the TickerClientHandler cla s when ending the initial quotes of
companie to the client on its watch li t.
61 2 • Distributed Computing
The run () method defines the body of the thread that handles a ticker client. It
waits for messages sent by its client and responds to two messages:
1. WATCH: This watch list is sent by the client at the beginning of the session. The
client handler responds by sending to the client the initial quotes of companies
on the watch list.
2. CLOSE: This message should be the last one received from the client. The client
handler responds by removing itself from the active-clients set QuotePush-
Server . clients and terminates itself.
_. ....
12.1 Socket-Based Communication • 61 3
break;
}
}
socket.close ();
in . close() ;
out . close () ;
} catch (I0Exception e) {
e . printStackTrace ();
}
QuotePushServer.clients.remove (this );
}
The newQuote () method pu he a new quote to the ticker client. Thi method is
not invoked by the client handler thread but by the quote-monitoring thread defined
in the run() method of the QuotePushServer clas [p. 611].
if (symbol[i] .equals(name)) {
needToSend = true;
break ;
}
} else {
needToSend = true;
}
if (needToSend) {
out . println(name + 11 11
+ quote);
out. flush();
}
}
This completes the stock quote server implementation using the server push
strategy.
The clients and servers are written by programmers. Stubs and skeletons are automat-
ically generated by the RMI compiler from the server code.
12.2 Ranote Method Invocation • 61 5
Figure 12.6
Client Server
The architecture
of remote method 1 6 3 4
invocation (RMI).
Stub 2 Skeleton
JVM JVM
5
Key Issues
In Java, there are actually two types of objects: ( 1) Objects that are accessible
only within the local host are called local objects or nonremote objects (by default,
objects are local); and (2) objects that are accessible from a remote host are called
remote objects, and are instances of classes that implement a marker interface called
j ava . rmi. Remote.
Remote objects and local objects are similar in the following respect : Reference
to local or remote objects can be passed as arguments and returned as result of method
invocation (local or remote). References to local or remote objects can be cast u ing
the same syntax. The instanceof operator can be applied to local or remote objects.
One difference between remote and local objects i that the client of remote
objects interact with the stubs representing the remote object, not the actual object .
Another difference between remote and local object is in the pa ing of arguments
and return values of remote method invocation . lf an argument or return value of a
remote method invocation is a local object, the object i erialized, ent to the remote
host, and deserialized (that is, a copy of the local object i pas ed to the remote host).
If an argument or return value of a remote method invocation i a remote object, a
remote object reference i pas ed. A remote object reference can uniquely identify
and locate a remote object in the network. In hort, in remote method invocation, local
objects are pa ed by value and remote objects are passed by reference.
An important question is, How doe a client locate the erver that will provide
the service? Thi question is resolved by use of a naming scheme coupled with the
RMI registry. Each RMI erver is identified by a URL with the protocol rmi. An RMI
registry i like a telephone directory. It contain a mapping between the RMI servers
and their names. Each RMI server mu t be regi tered to an RMI registry to make it
available to remote clients, a proce known a binding. A client can locate an RMI
server by contacting an RMI registry running on a remote host and looking up the
server by name. The following URL identifie an RMI server:
rmi: //host: pon I name
where host i the name or IP address of the host on which the RMI registry is running,
port is the port number of the RMI registry, and name is the name bound to the RMI
server.
The classes and interfaces related to RMI are contained in the j ava. rmi package.
The programming interface of the RMI registry is provided in the class Naming. All
the methods of the Naming class are static. They are summarized in the following
table:
Method Description
bind (name, obj) Binds the remote object obj to the specified name
rebind(name, obj ) Binds the remote object obj to the specified name,
even if the name is already bound. The old binding is
discarded
unbind(name) Removes the binding of the specified name
lookup(url) Returns the remote object bound to the specified URL
list(url) Returns a list of the bindings registered in the RMI
registry at the specified URL
The Contract interface must extend the Remote interface. The methods in
thjs interface must declare that they may throw the RemoteException excep-
tion. The types of the arguments and return values must be seriilizable.
2. Define a service implementation class that implements the Contract interface.
public class ServiceProvider extends UnicastRemoteObject,
implements Contract {
public String aService( . . . ) throws RemoteException {
II implementation .. .
}
II implementation of other services . . .
}
4. Generate the stub and skeleton classes, using the RMI compiler. The tub
class is named ServiceProvider_Stub, and the skeleton class is named
ServiceProvider_Skel. The stub class also implements the Contract in-
terface.
S. Develop a client that uses the service provided by the Contract interface. The
client can be a local or remote object. It must first locate the remote object that
provides the service before remote methods can be in oked:
The structure of the Proxy design patterns is shown in the following diagram:
RealObject \
realObject tj I
~Proxy
--~
The participants of the Proxy pattern are the following:
For a detailed discussion of the Proxy design pattern see Gamma et al. (1995].
Figure 12.7
Remote 11 UnicastRemoteObject I
6. d,
The typical struc-
ture of RMI appli-
cations. AClient > - - -- - - -~ Contract
aService()
I
r- -- ---- - L __ __- - ~
I I
I I
PURPOSE
This example shows the basic elements and steps involved in building an RMI
application.
DESCRIPTION
We develop an RMI server and an RMI client. The server sends a string "Hello from
Venus!" to its clients. The client, which is an applet, displays the string it receives
from the server.
SOLUTION
The Hello interface defines the service to be provided by the server. The Helloimpl
,, class is the server that implements the Hello interface. The HelloApplet class is
the client.
}
String sayHello () throws java.rmi.RemoteException;
The main O method of the Helloimpl class creates an instance of the erver
and binds it to the name HelloServer in the RMI registry.
The compilerrmic generates two files: Helloimpl_Stub. class (the stub) and
Helloimpl_Skel. class (the skeleton).
3. Start the RMI registry on the server host:
venus¼ rmiregistry k
The ini t () of the client applet looks up the server through the RMI registry on
the server host. It downcasts the server to Hello, the contract interface, and makes
remote method invocation obj. sayHello O.
In Lhis section we implement the stock ticker application by using RMI. We implement
both the client-pull and server-push strategies.
12.2 Remote Method Invocation • 621
Figure 12.8
Ticker
I Remote
&
I UnicastRemoteObject
The client pull de-
sign of stock ticker
applet and the QuoteSsrver
quote server, using TlckerPul~llentK>------t
RMI.
I
'- ----~I
* *
StockQuote QuoteServerlmpl
I
getQuote()
S7
Serializab/e I
The StockQuote class is a simple class that represents the quote of a stock.
Its instances are passed as the result of the get Quote O method, o it must be
serializable.
CIMs 8'octQuot•
import java. io.• ;
public class StockQuote implements Seriali zable {
public String name ;
public int quote;
public StockQuote (Stri ng name , i nt quote) {
this .name= name;
this.quote~ quote;
}
}
6 22 • Distributed Computing
The main () method creates an instance of Quote Server Impl and binds it to the
name QuoteServer. At the end, the moni torQuotes () method is invoked, which
continuously monitors the changes in the stock quotes. Unlike a socket server, an
RMI server does not need to wait for a client to contact the server. An RM1 client
will contact the RMI registry first, and a remote reference of the RMI server will be
passed to the client.
The moni torQuotes () method contains an infinite loop that simulates the
changes to the quotes with a random number generator.
12.2 Remote Method Invocation • 623
This completes the RMI stock quote server implementation using the client pull
strategy.
import java.awt.•;
import java.util.•;
import java.io.•;
import java.rmi .•;
public class TickerPullClient extends Ticker {
protected QuoteServer server ;
(The initQuotes () method on page 623)
(The updateQuotes O method on page 624)
}
In the ini tQuotes O method, the client looks up the quote erver.
} catch (Exception e) {
e .printStackTrace ();
}
}
62 4 ■ Distributed Computing
This completes the implementation of the RMI stock ticker client using the client
pull strategy.
import java.rmi.•;
public interface QuotePushServer extends QuoteServer {
void watch(QuoteClient client, String[] list)
throws RemoteException;
}
Figure 12.9
I Remote
LS
/ UnicastRemoteObject
TickerPushCllent OuotePushServer
newauoteo watchO
import java.rmi.*;
public interface QuoteClient extends Remote {
void newQuote (StockQuote newQuote)
throws RemoteException;
}
import java.util.*;
import java.rmi.•;
import java .rmi .server .UnicastRemoteObject;
public class QuotePushServerimpl extends UnicastRemoteObject
implements QuotePushServer {
public QuotePushServerimpl () throws java.rmi .RemoteE..~ception {}
protected StockQuote quote [)= {
nev StockQuote ( 11 IBM 11 • 100),
new StockQuote( 11 Sun11 , 100),
6 26 • Distributed Computing
The moni torQuotes () method simulates the changes to the quotes with a
random number generator. When the quote of a stock is changed, it iterates through
the clients set. For each client that is watching the stock, the newQuote () method
is invoked through the remote reference to the client.
This completes the RMI stock quote server implementation using the server push
strategy.
dllllt
import java.awt.•;
import java.util . *;
import java.io .•;
import java.rmi .•;
import java.rmi . server . •;
public class TickerPushClient extends Ticker
implements QuoteClient {
628 • Distributed Computing
"/QuotePushServer");
obj.watch(this, symbol);
StockQuote newQuote[] = obj.getQuote();
for ( int j = O; j < newQuote.length; j ++) {
for (inti= O; i < n; i++) {
if (newQuote[j] .name .equals(symbol[i])) {
quote[i] = Integer.toString(newQuote[j] .quote);
}
}
}
} catch (Exception e) {
e . printStackTrace();
}
}
This completes the implementation of the RMI stock ticker client using the server
push strategy.
Java database connectivity (JDBC) allows Java applications to interface with rela-
tional databases, which are widely used today. A wealth of information is stored in
relational databases. Through JDBC, Java applications can access information stored
in existing databases and share information with applications written in other lan-
guages.
Java database connectivity is a mechanism that allows Java programs to access
relational databases. It is a simple Java interface for Structured Query Language
(SQL), the standard language for accessing relational databases . The JDBC interface
supports full access to relational databases, including creating new tables, m odifying
existing tables, inserting and updating data in existing tables, querying a databa e,
and retrieving meta data (information about a database and its conte nts).
12.3 Java OatabaK Connectivity • 629
Driver Description
Command Description
3. For more informnlion about JDBC drivers and lheir u uilability, cupnbilitics, and limitations, see
http ://java . sun . com/ .
630 • Distributed Computing
The following SQL statement creates a new table called Beetles. It specifies
the columns that the table must have and declares a data type for each. For example,
the column specification name VARCHAR(35) defines a 35-character column called
name.
Note that the orderno column is missing from the Beetles table. We can add
the column to the table by using the SQL statement
After the table is built, rows are added to it with the INSERT command. The following
SQL statement adds a row to the Beetles table:
The following example would select every row from the Beetles table where the
name column contained the value "Michael Owen."
For the remainder of this section, familiarity with SQL and relational databases
is assumed. For detailed discussions of SQL and relational databases see Bowman
et al. (1996] and Darwen and Date (J 997].
An Overview of JDBC
The interfaces and classes of JDBC are contained in the j ava. sql package. The
commonly used JDBC interfaces and classes are summarized in the following table:
Interface/Class Description
Class.forName(JDBCDriverName);
For example, the following tatement load the JDBC- ODBC Bridge driver
provided by Sun Micro y tem :
JDBC uses URLs to locate databases. JDBC URLs are presented in one of two
formats :
■ For a database on the local machine:
jdbc :subprotocol :subname
Component Description
Method Description
The commonly used methods of the Statement interface are summarized in the
following table. The parameter sql is a string that represents a SQL statement.
Method Description
executeQuery(sql) Executes the statement sql that retrieves data from the
database and returns a ResultSet object
executeUpdate (sql) Executes the statement sql that updates the database (that
is, one of INSERT, UPDATE, or DELETE statements) and
returns an integer indicating the number of rows affected
by the statement
close O Releases the DBMS and any other JDBC re ources cur-
rently being used by this Statement object, immediately
rather than relying on automatic closure by the garbage
collector
Using the Beetles table example, the following statements create the table,
using JDBC:
String createString =
"CREATE TABLE Beetles (name VARCHAR(35), "+
"address VARCHAR(35), 11 +
"city VARCHAR(20), 11 +
"state VARCHAR (2) , 11 +
"zip VARCHAR(5), " +
"email VARCHAR(30), " +
"creditCard VARCHAR(15))";
stmt.executeUpdate (createString);
t'AILI 11.1
Methods of the Resul tSet Interface
Method Description
next() Advances to the next row of the result set; returns true if
successful or false when there are no more rows in the result
set
getType(i) Returns the value of the ith column. Type is the data type of the
column, and it can be one of
Byte, Boolean, Double, Float, Int, Long, or String
among others. The return value is of the corresponding type.
get Type (name) Same as the preceding method, except that it retrieves the values
of a column by its name
void close O Releases the DBMS and any other JDBC resources currently
being used by this ResultSet object
that is currently available for processing). Initially, the cursor is positioned just above
the first row of a Resul tSet object. Invoking next() moves the cursor to the first
row and makes it the current row. Subsequent calls to next () cause the cursor to
be moved down through the result set, one row at a time, from top to bottom. The
next() method will return false when there are no more rows. There are two ways
to identify the columns in the current row: by the column name or by the column index.
For example, getString(1) returns the first column of the result set as a String
object, and get String ( 11 name 11 ) returns the name column as a String object. The
following while loop iterates through the rows of the Resul tSet object, displaying
the name and email fields as it goes:
PURPOSE
Illustrate the basic concepts of JDBC.
DESCRIPTION
This program builds a new Beetles table, inserts a test record into the table, and queries
the table.
SOLUTION
BaDd JDBC Table
import java . sql .•;
import java . io .•;
import java.util.Date ;
public class BuildBeetlesTable {
public static void main (String args O)
throws SQLException, IOException {
System.out.print( 11 \n Loading JDBC-ODBC driver . .. \ n\ n") ;
try {
Class . f orName ( 11 sun . j dbc . odbc . J dbcOdbcDri ver 11 ) ;
} catch(ClassNotFoundException e) {
e.printStackTrace() ;
System.exit(l);
}
System.out.print( 11 Connecting to Orders database ... \n\n");
String url = 11 jdbc :odbc:DrdersDriver 11 ;
Connection conn=
DriverManager. getConnection (url, 11 rPasenko", "mpf98eub") ;
System .out .print ( 11 Building new Beetles table . .. \ n\ n");
String createString =
"CREATE TABLE Beetles (name VARCHAR (35) , 11 +
"address VARCHAR(35) , 11 +
"cit y VARCHAR (20), " +
"state VARCHAR (2), " +
"zip VARCRAR (5) , 11 +
"email VARCHAR(30), 11 +
"credi tCard VARCHAR(15))";
stmt . executeUpdate ( createStri ng);
System . out.print( "Inserting test r ow in Beetles table . . . \ n\ n") ;
String insertStri ng =
"INSERT INTO Beetles VALUES ('Michael Oven ' , " +
" ' 123 Elmvood Street ' , " +
"'Lake Forest 1 , " +
111 I L I . II +
II 65431 > II +
1 1
11 1
mowenOaol . com' , 11
+
111
MasterCard ') 11 ;
636 • Distributed Computing
stmt .executeUpdate(insertString);
ResultSet rset = stmt . executeQuery("SELECT * FROM Beetles");
PURPOSE
This example shows how to modify a table with JDBC.
DESCRIPTION
This program modifies the structure of the Beetles table, updates the test record in
that table, displays the modified table contents, and then deletes the test record.
SOLUTION
JDBC~Table
import java . util.Calendar;
import java . sql . *;
import java . io . •;
12.3 Java Database Connectivity • 63 7
class ModifyBeetlesTable {
public static void main( String args O )
throws SQLException, I OException {
try {
Class . forName("sun . jdbc . odbc . JdbcOdbcDriver") ;
} catch(ClassNotFoundException e) {
e.printStackTrace();
System . exit(! ) ;
}
System . out . print("\n Connecting to Beetles table ... \n\n" );
String url = "jdbc : odbc:OrdersDriver ";
Connection conn=
DriverManager .getConnection (url,"rPasenko" , "mpf98eub");
Statement stmt = conn.createStatement ();
System.out.print("Modifying Beetles table . .. \ n\ n ");
String updateString =
"ALTER TABLE Beetles ADD ORDERNO VARCHAR(12)" ;
stmt . executeUpdate(updateString) ;
Calendar cal= Calendar.getlnstance ();
String orderNo = String .valueOf (cal . get (cal .MONTH) +l ) +
String .valueOf (cal . get (cal.DAY_ OF_ MONTH)) +
String . valueOf (cal . get (cal . YEAR)) +
String.valueOf (cal . get (cal.HOUR)) +
String . valueOf (cal.get (cal . MINUTE)) +
String. valueOf (cal . get (cal .SECOND));
updateString = "UPDATE Beetles SET ORDERNO = " +
orderNo +"WHERE NAME= 'Michael Owen '";
stmt . executeUpdate (updateString);
ResultSet rset = stmt.executeQuery ( "SELECT * FROM Beetles");
System.out . print (" Displaying tabl e contents ... \ n\ n");
while (rset .next ()) {
System . out.println ( " " +
rset . getStri ng ("name" ) + ", 11 +
rset.getString (" address" ) +" 11
+
rset.getString ( "city") + ", " +
rset .getString (" state ") + "\n " +
rset .getStri ng ( 11 zi p 11 ) + ", 11 +
rset.getString (" email 11 ) + " , 11 +
rset . getString ( 11 creditCard") + 11 11 +
rset . getString (" orderno") );
}
System .out.print ( "\ n Del et ing t est re cord ... \ n\ n 11 ) ;
updateString = "DELETE FROM Beetl es WHERE NAME= ' Michael Owen '";
stmt . executeUpdate (updateStri ng);
System.out.print ( 11 Clos i ng connect ion . . . \ n\ n");
conn . commit O ;
stmt . close O ;
rset . cl os e O ;
conn . close O;
}
}
638 • Distributed Computing
PURPOSE
This example demonstrates database updates with JDBC.
DESCRIPTION
This example extends the Order applet from Example 8.13 so that it includes database
functionality. In this version, the applet writes each order to the Order database.
SOLUTION
We extend the Order class in Example 8.13. The OrderDialogclass will be replaced
by a new class JdbcOrderDialog.
dialogPanel. reset O ;
setVisible(false);
}
}
String insertString;
insertString • "INSERT INTO Beetles VALUES (" +
11
' " + dialogPanel. nameField . get Text () + 11 ' , " +
11
" ' + dialogPanel. addressFi eld . getTert () + " ' , " +
11
' " + dialogPanel . ci t yField . getText () + 11 ' , 11 +
" • " + dialogPanel. stateField . getText () + 11 ' , 11 +
11
' " + dialogPanel. zipField . getText () + 11 ' , 11 +
11
' " + dialogPanel . emailField . getText () + " ' , 11 +
o tn
+ creditCard + II I • II +
" ' " + orderNo + II I )11 j
stmt.executeUpdate (insertString);
stmt . close ();
conn .commit ();
conn . cl ose O;
}
}
}
640 • Distributed Computing
Socket-based communication and remote method invocation are adequate and ef-
fective mechanisms for distributed Java applications. However, the reality of today's
computing environment is that the majority of existing applications are written in lan-
guages other than Java. In this section we briefly discuss a mechanism for interfacing
Java applications with non-Java applications in distributed computing environments:
Common Object Request Broker Architecture (CORBA).
CORBA is an open, distributed object-computing infrastructure being standard-
ized by the Object Management Group (OMG), an industrial consortium that com-
prises 500 software vendors, developers, and users.
CORBA simplifies the development of distributed applications by providing a
unified view of all distributed systems. The CORBA application framework provides
interoperability between objects in heterogeneous distributed environments, where
the objects can be implemented in different languages, such as Java, CIC++, Ada,
and COBOL.
The basic architecture of CORBA resembles that of RMI. The participants in a
CORBA application are similar to participants in a Java RMI application:
Server: An object that provides services to objects residing on remote hosts.
Service contract: An interface that defines the services provided by the server.
Client: An object that uses services provided by objects residing on remote hosts.
Stub: An object that resides on the same host as the client and serves as a proxy,
or surrogate, of the remote server.
Skeleton: An object that resides on the same host as the server, receives requests
from the stubs, and dispatches the requests to the server.
The centerpiece of CORBA is the Interface Definition Language (IDL). The
CORBA clients and servers can be implemented in different languages. The ervice
contract interface of a CORBA server is defined in a language-neutral fa hion, u ing
IDL. The IDL compliers translate the interfaces written in IDL to variou implemen-
tation language , such as Java and CIC++. The IDL compilers also generate tub and
skeletons in various implementation languages.
A key component of the CORBA architecture is the Object Request Broker
(ORB). The ORB defines the mechanism and interfaces that enable objects to make
requests and receive respon es in a distributed environment. The ORB provides an
infrastructure allowing objects to communicate independently of ·pecific platform
Chapter Summary • 641
CHAPTER SUMMARY
F.URTHIR RIADINGI
Reese, G. (2000). Database Programming with JDBC and Java, 2nd ed., O'Reilly.
f IXIRCIIII
12.1 Use sockets to implement a two-way chat • A Send button for sending outgoing mes-
application consisting of a server and two sages
clients. The client program has a graphical • A Clear outgoing messages button
user interface that consists of the following: • A Clear incoming messages button
• A text area for typing outgoing messages • A Quit button
• Another text area for displaying incoming 12.2 Enhance Exercise J2.1 to support multiple
messages clients.
12.3 Use RMI to implement Exercises 12.1 and 12.2.
Projects • 643
PROJICTI '
12.1 Develop a distributed version of the tic-tac-toe 12.3 Develop a distributed version of the drawing
game in Section 11.3 (p. 571) using sockets or pad using sockets or RMI. Several drawing
RMJ. It should allow two players on different pad clients can run simultaneously on different
hosts to play the game remotely. hosts. Drawings by all the clients should be
12.2 Develop a distributed version of the multiplayer displayed on the same canvas and should be
tic-tac-toe game in Project I I. I [p. 585] using visible to all the clients.
sockets or RMI. It should allow multiple players
on different hosts to play the game remotely.
Summary of the APPLET Tag
The <applet> tag is used to embed a Java applet in an HTML page. The <applet>
tag has the following form:
<applet
(align = alignment]
[alt = alternate-text]
[archive = archived-file]
code = applet-filename
[code base = applet-url]
height = pixel-height
[hspace = horizontal-pixel-space]
[name = applet-name]
[vspace = vertical-pixel-space]
width = pixel-width
>
[<param name = param-name 1 value = param-value, >]
645
• Summary of the APPLET Tag
The attributes code, width, and height are required. The rest are optional. The
attributes of the <applet> tag are summarized in the following table:
Attribute Description
codebase Specifies the base URL of the applet. It is a relative path to the
directory containing the current HTML document. The default
value is the same directory as the current HTML document.
code Specifies the class name of the applet. The location of the class
file is specified by the code base attribute.
width Specifies the width of the applet's display area in pixels.
height Specifies the height of the applet's display area in pixels.
alt Specifies alternative text that should be displayed by browsers
that understand the <applet> tag but do not support Java.
name Specifies a name for the applet instance, making it possible for
applets on the same page to communicate with each other.
align Specifies the applet's position with respect to surrounding text.
vspace Specifies the top and bottom margins in pixels.
hspace Specifies the left and right margins in pixels.
param Specifies the parameters to the applet. Each parameter is
specified by a name- value pair. The param attribute may occur
multiple times.
archive Specifies a comma-separated list of Java archives (JAR files)
containing classes and other resources.
Documentation comments are special comments in Java source code that are delimited
by I** and *I. They are processed by the javadoc utility to generate API documen-
tation. The tags that can be used in the documentation comments are summarized a
follows :
@author name
Adds an Author entry to the generated docs when the - author option of
j avadoc is used. A doc comment may contain multiple Oauthor tags or
multiple names per tag.
@deprecated description
Adds a comment indicating that thi method has been deprecated since the
specified version. The first entence of the Odeprecated de cription should
tell the user when the method was deprecated and what can be used a. a
replacement. An Olink tag should be included that points to the replacement
method.
@exception name description
Adds an exception de cription to the £rceptions ection of the generated API
documentation. An Oexception tag hould be included for each checked
exception declared in the throw clau e of a method. It is synonymous with
the Othrows tag.
647
648 • Summary of Documentation Tags
@link name
Inserts a link that points to the specified name. Multiple link tags can be
used.
Oparam name description
Adds a parameter description to the Parameters section of the generated
API documentation. An @param tag should be included for each parameter
of a method.
Oreturn description
Adds a Returns section to the generated API documentation that describes
the return value of a method. The @return tag should be used for all
methods except methods that return void or constructors.
@see text
Adds a text entry. No link is generated.
@see package. class#member
Adds a link that points to a specific name in the API documentation.
Osee <a href=url>text</a>
Adds a link to the specified URL.
@serial description
Describes the serializable fields .
Osince text
Indicates the release or version number in which the feature was first
introduced.
@throws name description
Synonymous with the @exception tag.
@version text
Adds a Version section for specifying the version of the software that contains
this class or member when the -version option of j avadoc is used. Only
one @version tag is allowed per doc comment.
Summary of Java Naming
Conventions
Package Names
The first identifier of a package name con i ts of two or three letters that name an
Internet domain, such as com, edu, gov, mil, net, org. or a two-letter ISO country
code such as uk or jp. The following are ome example :
com . sun.java.corba
org.npr.pledge.driver
uk.ac.city .rugby .game
When defining package nan1e for local u e. you hould begin the first identifier
with a lowercase letter. The first identifier hould not be j ava, becau ·e it i reserved
for standard Java package .
649
650 • Summary of Java ~aming Conventions
ServletlnputStream
Thread
PrintStream
Runnable
Cloneable
Method Names
Verbs or verb phrases should be used for method names. In addition, method names
should consist of mixed case, with a lowercase first letter. The first letter of any
subsequent words should be capitalized. The following are examples:
write
play
showContent
■ When using a method to get or set an attribute thought of as variable Var, use
getVar and setVar. Examples are getDate and setDate.
■ When a method tests a boolean condition Cond about an object, it should be
named isCond. An example is isinterrupted.
Field Names
Field names (other than those that are final) should have names that are nouns, noun
phrases, or abbreviations of nouns. They should be in mixed case with a lowercase
first letter and the first letters of subsequent words capitalized. Examples include buf ,
pos, count, and bytesTransferred.
Constant Names
When naming the constants in interface types and the final variables of class types,
you should use a sequence of one or more words (or other appropriate parts of speech),
acronyms, or abbreviations. In addition, components should be all uppercase, and they
should be separated by the underscore character (_). Examples include MIN_ VALUE,
MAX_VALUE, MIN_RADIX, and MAX_RADIX.
■ an acronym, such as bg, for a variable holding a Color object used for the
background .
■ an abbreviation, such as buf, for a variable referring to a buffer.
■ a mnemonic term, such as i n and out , for input and output streams.
Summary of Jll'la Naming Conventions • 651
One-character local variable or parameter names should be used only for tempo-
rary and loop control variables, or where a variable holds an undistinguished value of
a type. Acceptable one-character names are as follows:
Name Type
b byte
C char
d double
e Exception
f float
i integer
j integer
k integer
1 long
0 Object
s String
V any type
You should not use local variable or parameter names that consi t of only two or
three uppercase letters, as they may conflict with the initial country codes and domain
names of unique package names.