0% found this document useful (0 votes)
69 views192 pages

Object Oriented Programming: David J. Eck Bradley P. Kjell Anban W. Pillay

This document provides an introduction to object-oriented programming (OOP). It defines OOP as a set of tools that enable building reliable, user-friendly software by modeling real-world problems as interacting objects. The key concepts of OOP discussed are objects, classes, inheritance, polymorphism, and dynamic binding. It explains that in OOP, programs are made of objects that receive and respond to messages, hiding internal details and promoting reusability.

Uploaded by

Mhlengi Wiseman
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
69 views192 pages

Object Oriented Programming: David J. Eck Bradley P. Kjell Anban W. Pillay

This document provides an introduction to object-oriented programming (OOP). It defines OOP as a set of tools that enable building reliable, user-friendly software by modeling real-world problems as interacting objects. The key concepts of OOP discussed are objects, classes, inheritance, polymorphism, and dynamic binding. It explains that in OOP, programs are made of objects that receive and respond to messages, hiding internal details and promoting reusability.

Uploaded by

Mhlengi Wiseman
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 192

Object Oriented Programming

David J. Eck
Bradley P. Kjell
Anban W. Pillay
Object Oriented Programming in Java

David J. Eck et.al


2018

i
Contents

Contents i

1 Introduction to Objects 1
1.1 What is Object Oriented Programming? 1
Object Orientation as a New Paradigm: The Big Picture 2
1.2 Fundamentals of Objects and Classes 4
Objects and Classes 4
Class Members and Instance Members 8
Access Control 15
Creating and Destroying Objects 16
Garbage Collection 23
Everything is NOT an object 24
1.3 Introduction to Error Handling 26
1.4 Javadoc 29
1.5 Creating Jar Files 32
APIs and Packages 34
1.6 Mixing Static and Non-static 39
Static Import 41

2 Object Oriented Analysis and Design 43


2.1 Software Engineering 43
Software Engineering Life-Cycles 44
Object Oriented Software Engineering 45
Abstraction and Information Hiding 46
2.2 A Simple Methodology 49

i
Contents

Discovering Classes: Class-Responsibility-Collaboration cards 49


2.3 Documenting Designs: The Unified Modelling Language 51
2.4 A Case Study: Card Games 62
Designing the classes 62
2.5 Example: A Simple Card Game 70

3 Inheritance 75
3.1 Introduction to Inheritance 75
3.2 Constructors in Sub-classes 80
3.3 Overriding Methods 83
Overloaded vs Overridden Methods 85
3.4 Another Example subclass 86
3.5 Inheritance and Types 88
Type Checking 90
Typecasting 90
3.6 Controlling Access to Members of a Class 92
Example 93

4 Abstract Classes, Interfaces and Inner Classes 99


4.1 Abstract Classes 99
4.2 Interfaces 106
Implementing an Interface 108
Example Problem 109
Type Casts 115
default methods in interfaces 117
Additional Facts about Interfaces 118
4.3 Nested Classes 119
Static Nested Classes 120
Inner Classes 121
Anonymous Inner Classes 122
Java 8 Lambda Expressions 123

5 Graphical User Interface Programming 125


5.1 The Basic GUI Application 125
JFrame and JPanel 126
Components and Layout 129
Events and Listeners 130
5.2 Graphics and Painting 132
Coordinates 134
Colors 135
Fonts 136
Shapes 137

ii
Contents

Graphics2D 139
An Example 140
5.3 Mouse Events 143
Event Handling 144
MouseEvent and MouseListener 146
MouseEvent Data 149
Anonymous Event Handlers 153
Timers 155
Timers and Animation 156
Keyboard Events 158
5.4 Basic Components 162
JButton 164
JLabel 165
JCheckBox 166
JTextField and JTextArea 167
5.5 Basic Layout 169
Basic Layout Managers 171
5.6 A Simple Calculator 174
Exercises for Chapter 5 177
Quiz on Chapter 5 184

iii
Chapter

1
Introduction to Objects

bject-oriented programming (OOP) represents an attempt to make pro-


O grams more closely model the way people think about and deal with the
Object-oriented
programming is one
world. In the older styles of programming, a programmer who is faced with of several
some problem must identify a computing task that needs to be performed in or- programming
der to solve the problem. Programming then consists of finding a sequence of paradigms
instructions that will accomplish that task. But at the heart of object-oriented
programming, instead of tasks we find objects – entities that have behaviors, that
hold information, and that can interact with one another. Programming consists
of designing a set of objects that model the problem at hand. Software objects in
the program can represent real or abstract entities in the problem domain. This
is supposed to make the design of the program more natural and hence easier to
get right and easier to understand.
An object-oriented programming language such as Java includes a number
of features that make it very different from a standard language. In order to make
effective use of those features, you have to “orient” your thinking correctly.

1.1 What is Object Oriented Programming?

bject-orientation 1 is a set of tools and methods that enable software


O engineers to build reliable, user friendly, maintainable, well documented,
Object Oriented
Programming
reusable software systems that fulfills the requirements of its users. It is claimed
that object-orientation provides software developers with new mind tools to use
1 This discussion is based on Chapter 2 of “An Introduction to Object-Oriented Programming”
by Timothy Budd.

1
. Introduction to Objects

in solving a wide variety of problems. Object-orientation provides a new view of


computation. A software system is seen as a community of objects that cooperate
with each other by passing messages in solving a problem.
An object-oriented programming laguage provides support for the following
object-oriented concepts:
Objects and Classes
Inheritance
Polymophism and Dynamic binding

Object Orientation as a New Paradigm: The Big Picture

It is claimed that the problem-solving techniques used in object-oriented pro-


gramming more closely models the way humans solve day-to-day problems.2
So lets consider how we solve an everyday problem: Suppose you wanted
to send flowers to a friend named Robin who lives in another city.To solve this
problem you simply walk to your nearest florist run by, lets say, Fred. You tell Fred
the kinds of flowers to send and the address to which they should be delivered.
You can be assured that the flowers will be delivered.
Now, lets examine the mechanisms used to solve your problem.

• You first found an appropriate agent (Fred, in this case) and you passed to
this agent a message containing a request.
• It is the responsibility of Fred to satisfy the request.
• There is some method (an algorithm or set of operations) used by Fred to
do this.
• You do not need to know the particular methods used to satisfy the request—
such information is hidden from view.

Off course, you do not want to know the details, but on investigation you
may find that Fred delivered a slightly different message to another florist in
the city where your friend Robin lives. That florist then passes another message
to a subordinate who makes the floral arrangement.The flowers, along with yet
another message, is passed onto a delivery person and so on. The florists also has
interactions with wholesalers who, in turn, had interactions with flower growers
and so on.
This leads to our first conceptual picture of object-oriented programming:
2 This discussion is based on Chapter 2 of An Introduction to Object-Oriented Programming
by Timothy Budd.

2
.. What is Object Oriented Programming?

An object-oriented program is structured as community of interacting


agents called objects. Each object has a role to play. Each object provides
a service or performs an action that is used by other members of the
community.

Messages and Responsibilities

Members of an object-oriented community make requests of each other. The


next important principle explains the use of messages to initiate action:

Action is initiated in object-oriented programming by the transmission of


a message to an agent (an object) responsible for the actions. The message
encodes the request for an action and is accompanied by any additional
information (arguments/parameters) needed to carry out the request. The
receiver is the object to whom the message is sent. If the receiver accepts
the message, it accepts responsibility to carry out the indicated action.
In response to a message, the receiver will perform some method to satisfy
the request.

There are some important issues to point out here:


• The client sending the request need not know the means by which the
request is carried out. In this we see the principle of information hiding.
• Another principle implicit in message passing is the idea of finding some-
one else to do the work i.e. reusing components that may have been written
by someone else.
• The interpretation of the message is determined by the receiver and can
vary with different receivers. For example, if you sent the message “deliver
flowers” to a friend, she will probably have understood what was required
and flowers would still have been delivered but the method she used would
have been very different from that used by the florist.
• In object-oriented programming, behaviour is described in terms of respon-
sibilities.
• Client’s requests for actions only indicates the desired outcome. The re-
ceivers are free to pursue any technique that achieves the desired outcomes.
• Thinking in this way allows greater independence between objects.
• Thus, objects have responsibilities that they are willing to fulfill on request.
The collection of reponsibilities associated with an object is often called a
protocol.

3
. Introduction to Objects

Classes and Instances (Objects)

The next important principle of object-oriented programming is

All objects are instances of a class. The method invoked by an object in


response to a message is determined by the class of the receiver. All objects
of a given class use the same method in response to similar messages.

Fred is an instance of a category or class of people i.e. Fred is an instance of a


class of florists. The term florist represents a class or category of all florists. Fred
is an object or instance of a class.
We interact with instances of a class but the class determines the behaviour
of instances. We can tell a lot about how Fred will behave by understanding how
Florists behave. We know, for example, that Fred, like all florists can arrange and
deliver flowers.
In the real world there is this distinction between classes and objects. Real-
world objects share two characteristics: They all have state and behavior. For
example, dogs have state (name, color, breed, hungry) and behavior (barking,
fetching, wagging tail). Students have state (name, student number, courses they
are registered for, gender) and behavior (take tests, attend courses, write tests,
party).

1.2 Fundamentals of Objects and Classes

We move now from the conceptual picture of objects and classes to a discussion
of software classes and objects.3
Objects are closely related to classes. A class can contain variables and meth-
ods. If an object is also a collection of variables and methods, how do they differ
from classes?

Objects and Classes

Objects

In object-oriented programming we create software objects that model real world


objects. Software objects are modeled after real-world objects in that they too
have state and behavior. A software object maintains its state in one or more
variables. A variable is an item of data named by an identifier. A software object
implements its behavior with methods. A method is a function associated with
an object.
3 This discussion is based on the “Object-oriented Programming Concepts” section of the Java
Tutorial by Sun MicroSystems.

4
.. Fundamentals of Objects and Classes

State
represented
by variables

Behavour
represented
by methods

Figure .: An Object

Definition: An object is a software bundle of variables and related meth-


ods.

An object is also known as an instance. An instance refers to a particular object.


For e.g. Karuna’s bicycle is an instance of a bicycle—It refers to a particular
bicycle. Sandile Zuma is an instance of a Student.
The variables of an object are formally known as instance variables because
they contain the state for a particular object or instance. In a running program,
there may be many instances of an object. For e.g. there may be many Student
objects. Each of these objects will have their own instance variables and each
object may have different values stored in their instance variables. For e.g. each
Student object will have a different number stored in its studentNumber variable.

Encapsulation

Object diagrams show that an object’s variables make up the center, or nucleus,
of the object. Methods surround and hide the object’s nucleus of variables from
other objects in the program. Packaging an object’s variables within the protec-
tive custody of its methods is called encapsulation.
Encapsulating related variables and methods into a neat software bundle is a
simple yet powerful idea that provides two benefits to software developers:

• Modularity: The source code for an object can be written and maintained
independently of the source code for other objects. Also, an object can be
easily passed around in the system. You can give your bicycle to someone
else, and it will still work.

• Information-hiding: An object has a public interface that other objects can


use to communicate with it. The object can maintain private information
and methods that can be changed at any time without affecting other ob-
jects that depend on it.

5
. Introduction to Objects

message

object A

object B

Figure .: A Message

Messages

Software objects interact and communicate with each other by sending messages
to each other. When object A wants object B to perform one of B’s methods,
object A sends a message to object B
There are three parts of a message: The three parts for the message
System.out.println("Hello World"); are:

• The object to which the message is addressed (System.out)


• The name of the method to perform (println)
• Any parameters needed by the method (”Hello World!”)

Classes

A class is a software blueprint for objects. A class is used to manufacture or


create objects. The class specifies the state of its objects by defining the instance
A class is a kind of
factory for
variables necessary to contain the state of every object. The class specifies the
constructing objects. behaviour of objects by declaring and providing implementations for the instance
The non-static methods necessary to operate on the state of the object.
parts of the class
specify, or describe, Definition: A class is a blueprint that defines the variables and the
what variables and methods common to all objects of a certain kind.
methods the objects
will contain. After you’ve created the class, you can create any number of objects from that
class. All the objects created from the class will have the same characteristics.
Objects are created and destroyed as the program runs, and there can be many
objects with the same structure, if they are created using the same class.

Types

Java, like most programming languages classifies values and expressions into types.
For e.g. String’s and int’s are types. A type basically specifies the allowed values
and allowed operations on values of that type.

6
.. Fundamentals of Objects and Classes

Definition: A type is a set of values together with one or more operations


that can be applied uniformly to all these values.

A type system basically gives meaning to collections of bits. Because any value
simply consists of a set of bits in a computer, the hardware makes no distinction
between memory addresses, instruction code, characters, integers and floating-
point numbers. Types inform programs and programmers how they should treat
those bits.
For example the integers are a type with values in the range −2, 147, 483, 648 t o +
2, 147, 483, 647 and various allowed operations that include addition, subtraction,
modulus etc.
The use of types by a programming language has several advantages:
• Safety. Use of types may allow a compiler to detect meaningless or invalid
code. For example, we can identify an expression ”Hello, World” / 3 as
invalid because one cannot divide a string literal by an integer. Strong
typing offers more safety.
• Optimization. Static type-checking may provide useful information to a
compiler. The compiler may then be able to generate more efficient code.
• Documentation. Types can serve as a form of documentation, since they
can illustrate the intent of the programmer. For instance, timestamps may
be a subtype of integers – but if a programmer declares a method as return-
ing a timestamp rather than merely an integer, this documents part of the
meaning of the method.
• Abstraction. Types allow programmers to think about programs at a higher
level, not bothering with low-level implementation. For example, program-
mers can think of strings as values instead of as a mere array of bytes.
There are fundamentally two kinds of types in Java: primitive types and refer-
ence types i.e. any variable you declare is either declared to be one of the primitive
types or a reference type. int, double and char are the built-in, primitive types
in Java.The primitive types can be used in various combinations to create other,
composite types. Every time we define a class, we are actually defining a new
type. For example, the Student class defined above introduces a new type. We
can now use this type like any other type: we can declare variables to be of this
type and we can use it as a type for parameters of methods.
Before a variable can be used, it must be declared. A declaration gives a
variable a name, a type and an initial value for e.g. int x = 8 declares x to be
of type int. All objects that we declare also have to be of a specified type–the
type of an object is the class from which it is created. Thus, when we declare
objects we state the type like so: Student st = new Student();. This statement

7
. Introduction to Objects

declares the variable st to be of type Student. This statement creates a new object
of the specified type and runs the Student constructor. The constructor’s job is
to properly initialize the object.
The String type is another example of an object type. Student and String
are composite types and give us the same advantages as the built-in types. The
ability to create our own types is a very powerful idea in modern languages.
When declaring variables, we can assign initial values. If you do not spec-
ify initial values, the compiler automatically assigns one: Instance variables of
numerical type (int, double, etc.) are automatically initialized to zero; boolean
variables are initialized to false; and char variables, to the Unicode character
with code number zero. The default initial value of object types is null.

Class Members and Instance Members

A class definition is made of members or components. A class can define variables


(or fields) and methods. Variables and methods can be static or non-static i.e. they
are defined with or without the keyword static.
e.g.
static double lastStudentNumber; //a static
member/variable/field
double studentNumber; //a non -static variable

static void printLastNumber() {...} //a static member/method


void printNumber() {...} //a non -static method

The non-static members of a class (variables and methods) are also known
as instance variables and methods while the static members are also known as
class variables and class methods. Each instance of a class (each object) gets its
own copy of all the instance variables defined in the class. When you create an
instance of a class, the system allocates enough memory for the object and all its
instance variables.
In addition to instance variables, classes can declare class variables (or static
variables). A class variable contains information that is shared by all instances
(objects) of the class. If one object changes the variable, it changes for all other
objects of that type. e.g. A Student number generator in a NewStudent class.
You can invoke a class method directly from the class, whereas you must
invoke instance methods on a particular instance. e.g. The methods in the Math
class are static and can be invoked without creating an instance of the Math class
for e.g. we can say Math.sqrt(x).
Consider a simple class whose job is to group together a few static member
variables for example a class could be used to store information about the person
who is using the program:
class UserData { static String name; static int age; }

8
.. Fundamentals of Objects and Classes

In programs that use this class, there is one copy each of the variables UserData.name
and UserData.age. There can only be one “user,” since we only have memory
space to store data about one user. The class, UserData, and the variables it con-
tains exist as long as the program runs. Now, consider a similar class that includes
non-static variables:
class PlayerData { String name; int age; }

In this case, there is no such variable as PlayerData.name or PlayerData.age,


since name and age are not static members of PlayerData. There is nothing much
in the class except the potential to create objects. But, it’s a lot of potential, since
it can be used to create any number of objects! Each object will have its own
variables called name and age. There can be many “players” because we can make
new objects to represent new players on demand. A program might use this
class to store information about multiple players in a game. Each player has a
name and an age. When a player joins the game, a new PlayerData object can be
created to represent that player. If a player leaves the game, the PlayerData object
that represents that player can be destroyed. A system of objects in the program
is being used to dynamically model what is happening in the game. You can’t do
this with “static” variables!
An object that belongs to a class is said to be an instance of that class and the
variables that the object contains are called instance variables. The methods
that the object contains are called instance methods.
For example, if the PlayerData class, is used to create an object, then that ob-
ject is an instance of the PlayerData class, and name and age are instance variables
in the object. It is important to remember that the class of an object determines
the types of the instance variables; however, the actual data is contained inside
the individual objects, not the class. Thus, each object has its own set of data.
The source code for methods are defined in the class yet it is better to think
of the instance methods as belonging to the object, not to the class. The non-
static methods in the class merely specify the instance methods that every object
created from the class will contain. For example a draw() method in two different
objects do the same thing in the sense that they both draw something. But there
is a real difference between the two methods—the things that they draw can be
different. You might say that the method definition in the class specifies what
type of behavior the objects will have, but the specific behavior can vary from
object to object, depending on the values of their instance variables.
The static and the non-static portions of a class are very different things and
serve very different purposes. Many classes contain only static members, or only
non-static. However, it is possible to mix static and non-static members in a
single class. The “static” definitions in the source code specify the things that are
part of the class itself, whereas the non-static definitions in the source code specify
things that will become part of every instance object that is created from the class.

9
. Introduction to Objects

Static member variables and static member methods in a class are sometimes
called class variables and class methods, since they belong to the class itself,
rather than to instances of that class.
So far, we’ve been talking mostly in generalities. Let’s now look at a specific
example to see how classes and objects work. Consider this extremely simpli-
fied version of a Student class, which could be used to store information about
students taking a course:
public class Student {

public String name; // Student 's name. public double test1 ,


test2, test3; // Grades on three tests.

public double getAverage() { // compute average test grade


return
(test1 + test2 + test3) / 3; }

} // end of class Student

None of the members of this class are declared to be static, so the class ex-
ists only for creating objects. This class definition says that any object that is an
instance of the Student class will include instance variables named name, test1,
test2, and test3, and it will include an instance method named getAverage().
The names and tests in different objects will generally have different values. When
called for a particular student, the method getAverage() will compute an average
using that student’s test grades. Different students can have different averages.
(Again, this is what it means to say that an instance method belongs to an indi-
vidual object, not to the class.)
In Java, a class is a type, similar to the built-in types such as int and boolean.
So, a class name can be used to specify the type of a variable in a declaration
statement, the type of a formal parameter, or the return type of a method. For
example, a program could define a variable named std of type Student with the
statement
Student std;

However, declaring a variable does not create an object! This is an important


point, which is related to this Very Important Fact:

In Java, no variable can ever hold an object. A variable can only hold a
reference to an object.

You should think of objects as floating around independently in the com-


puter’s memory. In fact, there is a special portion of memory called the heap
where objects live. Instead of holding an object itself, a variable holds the in-
formation necessary to find the object in memory. This information is called a

10
.. Fundamentals of Objects and Classes

reference or pointer to the object. In effect, a reference to an object is the address


of the memory location where the object is stored. When you use a variable
of class type, the computer uses the reference in the variable to find the actual
object.
In a program, objects are created using an operator called new, which creates
an object and returns a reference to that object. For example, assuming that std
is a variable of type Student, declared as above, the assignment statement
std = new Student();

would create a new object which is an instance of the class Student, and it would
store a reference to that object in the variable std. The value of the variable is a
reference to the object, not the object itself. It is not quite true to say that the
object is the “value of the variable std”. It is certainly not at all true to say that
the object is “stored in the variable std.” The proper terminology is that “the
variable std refers to the object,”.
So, suppose that the variable std refers to an object belonging to the class
Student. That object has instance variables name, test1, test2, and test3. These
instance variables can be referred to as std.name, std.test1, std.test2, and
std.test3. This follows the usual naming convention that when B is part of A,
then the full name of B is A.B. For example, a program might include the lines
System.out.println("Hello, " + std.name + ". Your test grades
are:");
System.out.println(std.test1);
System.out.println(std.test2);
System.out.println(std.test3);

This would output the name and test grades from the object to which std
refers. Similarly, std can be used to call the getAverage() instance method in
the object by saying std.getAverage(). To print out the student’s average, you
could say:
System.out.println( "Your average is " + std.getAverage() );

More generally, you could use std.name any place where a variable of type
String is legal. You can use it in expressions. You can assign a value to it. You
can pass it as a parameter to method. You can even use it to call methods from
the String class. For example, std.name.length() is the number of characters
in the student’s name.
It is possible for a variable like std, whose type is given by a class, to refer
to no object at all. We say in this case that std holds a null reference. The
null reference is written in Java as “null”. You can store a null reference in the
variable std by saying “std = null;” and you could test whether the value of
“std” is null by testing “if (std == null). . .”.

11
. Introduction to Objects

If the value of a variable is null, then it is, of course, illegal to refer to instance
variables or instance methods through that variable–since there is no object, and
hence no instance variables to refer to. For example, if the value of the variable st
is null, then it would be illegal to refer to std.test1. If your program attempts
to use a null reference illegally like this, the result is an error called a null pointer
exception.

Let’s look at a sequence of statements that work with objects:

Student std, std1, // Declare four variables of


std2, std3; // type Student.
std = new Student(); // Create a new object belonging
// to the class Student , and
// store a reference to that
// object in the variable std.
std1 = new Student(); // Create a second Student object
// and store a reference to
// it in the variable std1.
std2 = std1; // Copy the reference value in std1
// into the variable std2.
std3 = null; // Store a null reference in the
// variable std3.

std.name = "John Smith"; // Set values of some instance


variables.
std1.name = "Mary Jones";

// (Other instance variables have default


// initial values of zero .)

After the computer executes these statements, the situation in the computer’s
memory looks like this:

12
.. Fundamentals of Objects and Classes

This picture shows variables as little boxes, labeled with the names of the
variables. Objects are shown as boxes with round corners. When a variable
contains a reference to an object, the value of that variable is shown as an arrow
pointing to the object. The variable std3, with a value of null, doesn’t point
anywhere. The arrows from std1 and std2 both point to the same object. This
illustrates a Very Important Point:

When one object variable is assigned to another, only a reference is copied.


The object referred to is not copied.

When the assignment “std2 = std1;” was executed, no new object was cre-
ated. Instead, std2 was set to refer to the very same object that std1 refers to.
This has some consequences that might be surprising. For example, std1.name
and std2.name are two different names for the same variable, namely the instance
variable in the object that both std1 and std2 refer to. After the string “Mary
Jones” is assigned to the variable std1.name, it is also be true that the value of
std2.name is “Mary Jones”. There is a potential for a lot of confusion here, but
you can help protect yourself from it if you keep telling yourself, “The object is
not in the variable. The variable just holds a pointer to the object.”
You can test objects for equality and inequality using the operators == and !=,
but here again, the semantics are different from what you are used to. The test
“if (std1 == std2)”, tests whether the values stored in std1 and std2 are the
same. But the values are references to objects, not objects. So, you are testing

13
. Introduction to Objects

whether std1 and std2 refer to the same object, that is, whether they point to the
same location in memory. This is fine, if its what you want to do. But sometimes,
what you want to check is whether the instance variables in the objects have the
same values. To do that, you would need to ask whether
std1.test1 == std2.test1 && std1.test2 == std2.test2 &&
std1.test3
== std2.test3 && std1.name.equals(std2.name)}

I’ve remarked previously that Strings are objects, and I’ve shown the strings
“Mary Jones” and “John Smith” as objects in the above illustration. A variable
of type String can only hold a reference to a string, not the string itself. It could
also hold the value null, meaning that it does not refer to any string at all. This
explains why using the == operator to test strings for equality is not a good idea.
The fact that variables hold references to objects, not objects themselves, has a
couple of other consequences that you should be aware of. They follow logically,
if you just keep in mind the basic fact that the object is not stored in the variable.
The object is somewhere else; the variable points to it.
Suppose that a variable that refers to an object is declared to be final. This
means that the value stored in the variable can never be changed, once the variable
has been initialized. The value stored in the variable is a reference to the object.
So the variable will continue to refer to the same object as long as the variable
exists. However, this does not prevent the data in the object from changing. The
variable is final, not the object. It’s perfectly legal to say
final Student stu = new Student();

stu.name = "John Doe"; // Change data in the object;


// The value stored in stu is not
changed!
// It still refers to the same object.

Next, suppose that obj is a variable that refers to an object. Let’s consider
what happens when obj is passed as an actual parameter to a method. The value of
obj is assigned to a formal parameter in the method, and the method is executed.
The method has no power to change the value stored in the variable, obj. It only
has a copy of that value. However, that value is a reference to an object. Since the
method has a reference to the object, it can change the data stored in the object.
After the method ends, obj still points to the same object, but the data stored in
the object might have changed. Suppose x is a variable of type int and stu is a
variable of type Student. Compare:
void dontChange(int z) { void change(Student s) {
z = 42; s.name = "Fred";
} }

14
.. Fundamentals of Objects and Classes

The lines: The lines:

x = 17; stu.name = "Jane";


dontChange(x); change(stu);
System.out.println(x); System.out.println(stu.name);

outputs the value 17. outputs the value "Fred".

The value of x is not The value of stu is not


changed ,
changed by the method, but stu.name is.
which is equivalent to This is equivalent to

z = x; s = stu;
z = 42; s.name = "Fred";

Access Control

When writing new classes, it’s a good idea to pay attention to the issue of access
control. Recall that making a member of a class public makes it accessible from
anywhere, including from other classes. On the other hand, a private member
can only be used in the class where it is defined.
In the opinion of many programmers, almost all member variables should be
declared private. This gives you complete control over what can be done with
the variable. Even if the variable itself is private, you can allow other classes to
find out what its value is by providing a public accessor method that returns
the value of the variable. For example, if your class contains a private member
variable, title, of type String, you can provide a method
public String getTitle() { return title; }

that returns the value of title. By convention, the name of an accessor method
for a variable is obtained by capitalizing the name of variable and adding “get”
in front of the name. So, for the variable title, we get an accessor method
named “get” + “Title”, or getTitle(). Because of this naming convention, ac-
cessor methods are more often referred to as getter methods. A getter method
provides “read access” to a variable.
You might also want to allow “write access” to a private variable. That is,
you might want to make it possible for other classes to specify a new value for
the variable. This is done with a setter method. (If you don’t like simple, Anglo-
Saxon words, you can use the fancier term mutator method.) The name of a setter
method should consist of “set” followed by a capitalized copy of the variable’s
name, and it should have a parameter with the same type as the variable. A setter
method for the variable title could be written

15
. Introduction to Objects

public void setTitle( String newTitle ) { title = newTitle; }

It is actually very common to provide both a getter and a setter method for
a private member variable. Since this allows other classes both to see and to
change the value of the variable, you might wonder why not just make the vari-
able public? The reason is that getters and setters are not restricted to simply
reading and writing the variable’s value. In fact, they can take any action at all.
For example, a getter method might keep track of the number of times that the
variable has been accessed:
public String getTitle() {
titleAccessCount++; // Increment member variable
titleAccessCount .
return title;
}

and a setter method might check that the value that is being assigned to the
variable is legal:
public void setTitle( String newTitle ) {
if ( newTitle == null ) //Don't allow null strings as titles!
title = "(Untitled)"; // Use an appropriate default value
instead.
else
title = newTitle; }

Even if you can’t think of any extra chores to do in a getter or setter method,
you might change your mind in the future when you redesign and improve your
class. If you’ve used a getter and setter from the beginning, you can make the
modification to your class without affecting any of the classes that use your class.
The private member variable is not part of the public interface of your class;
only the public getter and setter methods are. If you haven’t used get and set
from the beginning, you’ll have to contact everyone who uses your class and tell
them, “Sorry guys, you’ll have to track down every use that you’ve made of this
variable and change your code.”

Creating and Destroying Objects

Object types in Java are very different from the primitive types. Simply declaring
a variable whose type is given as a class does not automatically create an object
of that class. Objects must be explicitly constructed. For the computer, the
process of constructing an object means, first, finding some unused memory in
the heap that can be used to hold the object and, second, filling in the object’s
instance variables. As a programmer, you don’t care where in memory the object
is stored, but you will usually want to exercise some control over what initial
values are stored in a new object’s instance variables. In many cases, you will also

16
.. Fundamentals of Objects and Classes

want to do more complicated initialization or bookkeeping every time an object


is created.

Initializing Instance Variables

An instance variable can be assigned an initial value in its declaration, just like
any other variable. For example, consider a class named PairOfDice. An object
of this class will represent a pair of dice. It will contain two instance variables to
represent the numbers showing on the dice and an instance method for rolling
the dice:
public class PairOfDice {

public int die1 = 3; // Number showing on the first die.


public int die2 = 4; // Number showing on the second die.

public void roll() {


// Roll the dice by setting each of the dice to be
// a random number between 1 and 6.
die1 = (int)(Math.random()*6) + 1;
die2 = (int)(Math.random()*6) + 1;
}

} // end class PairOfDice

The instance variables die1 and die2 are initialized to the values 3 and 4 re-
spectively. These initializations are executed whenever a PairOfDice object is
constructed. It is important to understand when and how this happens. Many
PairOfDice objects may exist. Each time one is created, it gets its own instance
variables, and the assignments “die1 = 3” and “die2 = 4” are executed to fill in
the values of those variables. To make this clearer, consider a variation of the
PairOfDice class:

public class PairOfDice {

public int die1 = (int)(Math.random()*6) + 1;


public int die2 = (int)(Math.random()*6) + 1;

public void roll() {


die1 = (int)(Math.random()*6) + 1;
die2 = (int)(Math.random()*6) + 1;
}

} // end class PairOfDice

Here, the dice are initialized to random values, as if a new pair of dice were being
thrown onto the gaming table. Since the initialization is executed for each new

17
. Introduction to Objects

object, a set of random initial values will be computed for each new pair of dice.
Different pairs of dice can have different initial values. For initialization of static
member variables, of course, the situation is quite different. There is only one
copy of a static variable, and initialization of that variable is executed just once,
when the class is first loaded.
If you don’t provide any initial value for an instance variable, a default ini-
tial value is provided automatically. Instance variables of numerical type (int,
double, etc.) are automatically initialized to zero if you provide no other values;
boolean variables are initialized to false; and char variables, to the Unicode
character with code number zero. An instance variable can also be a variable of
object type. For such variables, the default initial value is null. (In particular,
since Strings are objects, the default initial value for String variables is null.)

Constructors

Objects are created with the operator, new. For example, a program that wants
to use a PairOfDice object could say:
PairOfDice dice; // Declare a variable of type PairOfDice.

dice = new PairOfDice(); // Construct a new object and store a


// reference to it in the variable.

In this example, “new PairOfDice()” is an expression that allocates memory


for the object, initializes the object’s instance variables, and then returns a refer-
ence to the object. This reference is the value of the expression, and that value is
stored by the assignment statement in the variable, dice, so that after the assign-
ment statement is executed, dice refers to the newly created object. Part of this
expression, “PairOfDice()”, looks like a method call, and that is no accident. It
is, in fact, a call to a special type of method called a constructor. This might
puzzle you, since there is no such method in the class definition. However, every
class has at least one constructor. If the programmer doesn’t write a construc-
tor definition in a class, then the system will provide a default constructor
for that class. This default constructor does nothing beyond the basics: allocate
memory and initialize instance variables. If you want more than that to happen
when an object is created, you can include one or more constructors in the class
definition.
The definition of a constructor looks much like the definition of any other
method, with three differences.

1. A constructor does not have any return type (not even void).

2. The name of the constructor must be the same as the name of the class in
which it is defined.

18
.. Fundamentals of Objects and Classes

3. The only modifiers that can be used on a constructor definition are the
access modifiers public, private, and protected. (In particular, a con-
structor can’t be declared static.)
However, a constructor does have a method body of the usual form, a block
of statements. There are no restrictions on what statements can be used. And it
can have a list of formal parameters. In fact, the ability to include parameters is
one of the main reasons for using constructors. The parameters can provide data
to be used in the construction of the object. For example, a constructor for the
PairOfDice class could provide the values that are initially showing on the dice.
Here is what the class would look like in that case:
The constructor is declared as “public PairOfDice(int val1, int val2)...”,
with no return type and with the same name as the name of the class. This is how
the Java compiler recognizes a constructor. The constructor has two parameters,
and values for these parameters must be provided when the constructor is called.
For example, the expression “new PairOfDice(3,4)” would create a PairOfDice
object in which the values of the instance variables die1 and die2 are initially 3
and 4. Of course, in a program, the value returned by the constructor should be
used in some way, as in
PairOfDice dice; // Declare a variable of type PairOfDice.

dice = new PairOfDice(1,1); // Let dice refer to a new


PairOfDice
// object that initially shows 1,
1.

Now that we’ve added a constructor to the PairOfDice class, we can no longer
create an object by saying “new PairOfDice()”! The system provides a default
constructor for a class only if the class definition does not already include a con-
structor, so there is only one constructor in the class, and it requires two actual
parameters. However, this is not a big problem, since we can add a second con-
structor to the class, one that has no parameters. In fact, you can have as many
different constructors as you want, as long as their signatures are different, that
is, as long as they have different numbers or types of formal parameters. In the
PairOfDice class, we might have a constructor with no parameters which pro-
duces a pair of dice showing random numbers:
public class PairOfDice {

public int die1; // Number showing on the first die.


public int die2; // Number showing on the second die.

public PairOfDice() {
// Constructor. Rolls the dice , so that they
initially

19
. Introduction to Objects

// show some random values.


roll(); // Call the roll () method to roll the dice.
}

public PairOfDice(int val1, int val2) {


// Constructor. Creates a pair of dice that
// are initially showing the values val1 and val2.
die1 = val1; // Assign specified values
die2 = val2; // to the instance variables.
}

public void roll() {


// Roll the dice by setting each of the dice to be
// a random number between 1 and 6.
die1 = (int)(Math.random()*6) + 1;
die2 = (int)(Math.random()*6) + 1;
}

} // end class PairOfDice

Now we have the option of constructing a PairOfDice object with “new


PairOfDice()” or with “new PairOfDice(x,y)”, where x and y are int-valued
expressions.
This class, once it is written, can be used in any program that needs to work
with one or more pairs of dice. None of those programs will ever have to use
the obscure incantation “(int)(Math.random()*6)+1”, because it’s done inside
the PairOfDice class. And the programmer, having once gotten the dice-rolling
thing straight will never have to worry about it again. Here, for example, is a
main program that uses the PairOfDice class to count how many times two pairs
of dice are rolled before the two pairs come up showing the same value. This
illustrates once again that you can create several instances of the same class:
public class RollTwoPairs {

public static void main(String[] args) {

PairOfDice firstDice; // Refers to the first pair of


dice.
firstDice = new PairOfDice();

PairOfDice secondDice; // Refers to the second pair of


dice.
secondDice = new PairOfDice();

int countRolls; // Counts how many times the two pairs


of
// dice have been rolled.

20
.. Fundamentals of Objects and Classes

int total1; // Total showing on first pair of dice.


int total2; // Total showing on second pair of dice.

countRolls = 0;

do { // Roll the two pairs of dice until totals are the


same.

firstDice.roll(); // Roll the first pair of dice.


total1 = firstDice.die1 + firstDice.die2; // Get
total.
System.out.println("First pair comes up " + total1);

secondDice.roll(); // Roll the second pair of


dice.
total2 = secondDice.die1 + secondDice.die2; // Get
total.
System.out.println("Second pair comes up " + total2);

countRolls++; // Count this roll.

System.out.println(); // Blank line.

} while (total1 != total2);

System.out.println("It took " + countRolls


+ " rolls until the totals were the
same.");

} // end main ()

} // end class RollTwoPairs


Constructors are methods, but they are methods of a special type. They are
certainly not instance methods, since they don’t belong to objects. Since they are
responsible for creating objects, they exist before any objects have been created.
They are more like static member methods, but they are not and cannot be
declared to be static. In fact, according to the Java language specification, they
are technically not members of the class at all! In particular, constructors are not
referred to as “methods”.
Unlike other methods, a constructor can only be called using the new operator,
in an expression that has the form
new class-name{parameter-list}
where the parameter-list is possibly empty. I call this an expression because it
computes and returns a value, namely a reference to the object that is constructed.

21
. Introduction to Objects

Most often, you will store the returned reference in a variable, but it is also legal
to use a constructor call in other ways, for example as a parameter in a method
call or as part of a more complex expression. Of course, if you don’t save the
reference in a variable, you won’t have any way of referring to the object that was
just created.
A constructor call is more complicated than an ordinary method call. It is
helpful to understand the exact steps that the computer goes through to execute
a constructor call:
1. First, the computer gets a block of unused memory in the heap, large
enough to hold an object of the specified type.
2. It initializes the instance variables of the object. If the declaration of an
instance variable specifies an initial value, then that value is computed and
stored in the instance variable. Otherwise, the default initial value is used.
3. The actual parameters in the constructor, if any, are evaluated, and the
values are assigned to the formal parameters of the constructor.
4. The statements in the body of the constructor, if any, are executed.
5. A reference to the object is returned as the value of the constructor call.
The end result of this is that you have a reference to a newly constructed object.
You can use this reference to get at the instance variables in that object or to call
its instance methods.
For another example, let’s rewrite the Student class. I’ll add a constructor,
and I’ll also take the opportunity to make the instance variable, name, private.
public class Student {
private String name; // Student 's name.
public double test1, test2, test3; // Grades on three tests.

// Constructor for Student objects -provides a name for the


Student.
Student(String theName) {
name = theName;
}

// Getter method for the private instance variable , name.


public String getName() {
return name;
}

// Compute average test grade.


public double getAverage() {

22
.. Fundamentals of Objects and Classes

return (test1 + test2 + test3) / 3;


}
} // end of class Student

An object of type Student contains information about some particular stu-


dent. The constructor in this class has a parameter of type String, which specifies
the name of that student. Objects of type Student can be created with statements
such as:
std = new Student("John Smith");
std1 = new Student("Mary Jones");

In the original version of this class, the value of name had to be assigned by a
program after it created the object of type Student. There was no guarantee that
the programmer would always remember to set the name properly. In the new
version of the class, there is no way to create a Student object except by calling the
constructor, and that constructor automatically sets the name. The programmer’s
life is made easier, and whole hordes of frustrating bugs are squashed before they
even have a chance to be born.
Another type of guarantee is provided by the private modifier. Since the
instance variable, name, is private, there is no way for any part of the program
outside the Student class to get at the name directly. The program sets the value of
name, indirectly, when it calls the constructor. I’ve provided a method, getName(),
that can be used from outside the class to find out the name of the student. But
I haven’t provided any setter method or other way to change the name. Once a
student object is created, it keeps the same name as long as it exists.

Garbage Collection

So far, this section has been about creating objects. What about destroying them?
In Java, the destruction of objects takes place automatically.
An object exists in the heap, and it can be accessed only through variables
that hold references to the object. What should be done with an object if there
are no variables that refer to it? Such things can happen. Consider the following
two statements (though in reality, you’d never do anything like this):
Student std = new Student("John Smith"); std = null;

In the first line, a reference to a newly created Student object is stored in the
variable std. But in the next line, the value of std is changed, and the reference
to the Student object is gone. In fact, there are now no references whatsoever to
that object stored in any variable. So there is no way for the program ever to use
the object again. It might as well not exist. In fact, the memory occupied by the
object should be reclaimed to be used for another purpose.
Java uses a procedure called garbage collection to reclaim memory occupied
by objects that are no longer accessible to a program. It is the responsibility of

23
. Introduction to Objects

the system, not the programmer, to keep track of which objects are “garbage”. In
the above example, it was very easy to see that the Student object had become
garbage. Usually, it’s much harder. If an object has been used for a while, there
might be several references to the object stored in several variables. The object
doesn’t become garbage until all those references have been dropped.
In many other programming languages, it’s the programmer’s responsibility
to delete the garbage. Unfortunately, keeping track of memory usage is very error-
prone, and many serious program bugs are caused by such errors. A programmer
might accidently delete an object even though there are still references to that
object. This is called a dangling pointer error, and it leads to problems when
the program tries to access an object that is no longer there. Another type of
error is a memory leak, where a programmer neglects to delete objects that are no
longer in use. This can lead to filling memory with objects that are completely
inaccessible, and the program might run out of memory even though, in fact,
large amounts of memory are being wasted.
Because Java uses garbage collection, such errors are simply impossible. Garbage
collection is an old idea and has been used in some programming languages since
the 1960s. You might wonder why all languages don’t use garbage collection. In
the past, it was considered too slow and wasteful. However, research into garbage
collection techniques combined with the incredible speed of modern computers
have combined to make garbage collection feasible. Programmers should rejoice.

Everything is NOT an object

Wrapper Classes and Autoboxing

Recall that there are two kinds of types in Java: primitive types and object types
(Classes). In some object-oriented languages, everything is an object. However
in Java and in C++, the primitive types like int and double are not objects. This
decision was made for memory and processing efficiency—it takes less memory
to store an int than it is to store an object.
Sometimes, however, it is necessary to manipulate the primitive types as if
they were objects. To make this possible, you can define wrapper classes whose
sole aim is to contain one of the primitive types. They are used for creating
objects that represent primitive type values.
For example the Java API contains the classes Double (that wraps a single
double) and Integer that wraps a single integer. These classes contain vari-
ous static methods including Double.parseDouble and Integer.parseInteger
that are used to convert strings to numerical values. The Character class wraps
a single char type. There is a similar class for each of the other primitive types,
Long, Short, Byte, Float, and Boolean.

24
.. Fundamentals of Objects and Classes

Remember that the primitive types are not classes, and values of primitive
type are not objects. However, sometimes it’s useful to treat a primitive value as
if it were an object. You can’t do that literally, but you can “wrap” the primitive
type value in an object belonging to one of the wrapper classes.
For example, an object of type Double contains a single instance variable, of
type double. The object is a wrapper for the double value. For example, you can
create an object that wraps the double value 6.0221415e23 with
Double d = new Double(6.0221415e23);

The value of d contains the same information as the value of type double, but
it is an object. If you want to retrieve the double value that is wrapped in the
object, you can call the method d.doubleValue(). Similarly, you can wrap an
int in an object of type Integer, a boolean value in an object of type Boolean,
and so on. (As an example of where this would be useful, the collection classes
that will be studied in Chapter 10 can only hold objects. If you want to add a
primitive type value to a collection, it has to be put into a wrapper object first.)
In Java 5.0, wrapper classes have become easier to use. Java 5.0 introduced
automatic conversion between a primitive type and the corresponding wrapper
class. For example, if you use a value of type int in a context that requires an
object of type Integer, the int will automatically be wrapped in an Integer
object. For example, you can say Integer answer = 42; and the computer will
silently read this as if it were Integer answer = new Integer(42);.
This is called autoboxing. It works in the other direction, too. For example,
if d refers to an object of type Double, you can use d in a numerical expression
such as 2*d. The double value inside d is automatically unboxed and multiplied
by 2. Autoboxing and unboxing also apply to method calls. For example, you
can pass an actual parameter of type int to a method that has a formal parameter
of type Integer. In fact, autoboxing and unboxing make it possible in many
circumstances to ignore the difference between primitive types and objects.
The wrapper classes contain a few other things that deserve to be mentioned.
Integer contains constants Integer.MIN_VALUE and Integer.MAX_VALUE, which
are equal to the largest and smallest possible values of type int, that is, to −2147483648
and 2147483647 respectively. It’s certainly easier to remember the names than
the numerical values. There are similar named constants in Long, Short, and
Byte. Double and Float also have constants named MIN_VALUE and MAX_VALUE.
MAX_VALUE still gives the largest number that can be represented in the given type,
but MIN_VALUE represents the smallest possible positive value. For type double,
Double.MIN_VALUE is 4.9×10−324 . Since double values have only a finite accuracy,
they can’t get arbitrarily close to zero. This is the closest they can get without ac-
tually being equal to zero.
The class Double deserves special mention, since doubles are so much more
complicated than integers. The encoding of real numbers into values of type

25
. Introduction to Objects

double has room for a few special values that are not real numbers at all in the
mathematical sense. These values named constants in the class: Double.POSITIVE_INFINITY,
Double.NEGATIVE_INFINITY, and Double.NaN. The infinite values can occur as
values of certain mathematical expressions. For example, dividing a positive
number by zero will give Double.POSITIVE_INFINITY. (It’s even more compli-
cated than this, actually, because the double type includes a value called “neg-
ative zero”, written -0.0. Dividing a positive number by negative zero gives
Double.NEGATIVE_INFINITY.) You also get Double.POSITIVE_INFINITY when-
ever the mathematical value of an expression is greater than Double.MAX_VALUE.
For example, 1e200*1e200 is considered to be infinite. The value Double.NaN is
even more interesting. “NaN” stands for Not a Number, and it represents an un-
defined value such as the square root of a negative number or the result of dividing
zero by zero. Because of the existence of Double.NaN, no mathematical operation
on real numbers will ever throw an exception; it simply gives Double.NaN as the
result.
You can test whether a value, x, of type double is infinite or undefined by call-
ing the boolean-valued static methods Double.isInfinite(x) and Double.isNaN().
(It’s especially important to use Double.isNaN() to test for undefined values, be-
cause Double.NaN has really weird behavior when used with relational operators
such as ==. In fact, the values of x == Double.NaN and x != Double.NaN are both
false, no matter what the value of x, so you really can’t use these expressions to
test whether x is Double.NaN.)

1.3 Introduction to Error Handling

n addition to the control structures that determine the normal flow of


I control in a program, Java has a way to deal with “exceptional” cases that
throw the flow of control off its normal track. When an error occurs during the
execution of a program, the default behavior is to terminate the program and to
print an error message. However, Java makes it possible to “catch” such errors
and program a response different from simply letting the program crash. This
is done with the try..catch statement. In this section, we will take a preliminary,
incomplete look at using try..catch to handle errors.

Exceptions

The term exception is used to refer to the type of error that one might want to
handle with a try..catch. An exception is an exception to the normal flow of
control in the program. The term is used in preference to “error” because in
some cases, an exception might not be considered to be an error at all. You can
sometimes think of an exception as just another way to organize a program.

26
.. Introduction to Error Handling

Exceptions in Java are represented as objects of type Exception. Actual ex-


ceptions are defined by subclasses of Exception. Different subclasses represent
different types of exceptions We will look at only two types of exception in this
section: NumberFormatException and IllegalArgumentException.
A NumberFormatException can occur when an attempt is made to convert a
string into a number. Such conversions are done, for example, by Integer.parseInt
and Integer.parseDouble . Consider the method call Integer.parseInt(str)
where str is a variable of type String. If the value of str is the string “42”, then
the method call will correctly convert the string into the int 42. However, if
the value of str is, say, “fred”, the method call will fail because “fred” is not
a legal string representation of an int value. In this case, an exception of type
NumberFormatException occurs. If nothing is done to handle the exception, the
program will crash.
An IllegalArgumentException can occur when an illegal value is passed as
a parameter to a method. For example, if a method requires that a parameter be
greater than or equal to zero, an IllegalArgumentException might occur when
a negative value is passed to the method. How to respond to the illegal value is
up to the person who wrote the method, so we can’t simply say that every illegal
parameter value will result in an IllegalArgumentException. However, it is a
common response.
One case where an IllegalArgumentException can occur is in the valueOf
method of an enumerated type. Recall that this method tries to convert a string
into one of the values of the enumerated type. If the string that is passed as a
parameter to valueOf is not the name of one of the enumerated type’s value, then
an IllegalArgumentException occurs. For example, given the enumerated type
enum Toss { HEADS, TAILS };

Toss.valueOf("HEADS") correctly returns Toss.HEADS, but Toss.valueOf(``FEET'')


results in an IllegalArgumentException.

try …catch

When an exception occurs, we say that the exception is “thrown”. For example,
we say that Integer.parseInt(str) throws an exception of type NumberFormatException
when the value of str is illegal. When an exception is thrown, it is possible to
“catch” the exception and prevent it from crashing the program. This is done
with a try..catch statement. In somewhat simplified form, the syntax for a
try..catch is:

27
. Introduction to Objects

try {
statements-1
}
catch ( exception-class-name variable-name ) {
statements-2
}

The exception-class-name in the catch clause could be NumberFormatException,


IllegalArgumentException, or some other exception class. When the computer
executes this statement, it executes the statements in the try part. If no error
occurs during the execution of statements-1, then the computer just skips over
the catch part and proceeds with the rest of the program. However, if an excep-
tion of type exception-class-name occurs during the execution of statements-1,
the computer immediately jumps to the catch part and executes statements-2,
skipping any remaining statements in statements-1. During the execution of
statements-2, the variable-name represents the exception object, so that you can,
for example, print it out. At the end of the catch part, the computer proceeds
with the rest of the program; the exception has been caught and handled and
does not crash the program. Note that only one type of exception is caught; if
some other type of exception occurs during the execution of statements-1, it
will crash the program as usual.
(By the way, note that the braces, are part of the syntax of the try..catch
statement. They are required even if there is only one statement between the
braces. This is different from the other statements we have seen, where the braces
around a single statement are optional.)
As an example, suppose that str is a variable of type String whose value
might or might not represent a legal real number. Then we could say:
try {
double x;
x = Double.parseDouble(str);
System.out.println( "The number is " + x );
}
catch ( NumberFormatException e ) {
System.out.println( "Not a legal number." );
}

If an error is thrown by the call to Double.parseDouble(str), then the out-


put statement in the try part is skipped, and the statement in the catch part is
executed.
It’s not always a good idea to catch exceptions and continue with the program.
Often that can just lead to an even bigger mess later on, and it might be better
just to let the exception crash the program at the point where it occurs. However,
sometimes it’s possible to recover from an error. For example, suppose that we
have the enumerated type

28
.. Javadoc

enum Day {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY,


SATURDAY, SUNDAY}

and we want the user to input a value belonging to this type. TextIO does not
know about this type, so we can only read the user’s response as a string. The
method Day.valueOf can be used to convert the user’s response to a value of type
Day. This will throw an exception of type IllegalArgumentException if the user’s
response is not the name of one of the values of type Day, but we can respond
to the error easily enough by asking the user to enter another response. Here is
a code segment that does this. (Converting the user’s response to upper case will
allow responses such as “Monday” or “monday” in addition to “MONDAY”.)
Scanner keyboard = new Scanner(System.in);
Day weekday; // User 's response as a value of type Day.
while ( true ) {
String response; // User 's response as a String.
keyboard.put("Please enter a day of the week: ");
response = keyboard.nextLinen();
response = response.toUpperCase();
try {
weekday = Day.valueOf(response);
break;
}
catch ( IllegalArgumentException e ) {
System.out.println( response +
" is not the name of a day of the week." );
}
}

The break statement will be reached only if the user’s response is acceptable,
and so the loop will end only when a legal value has been assigned to weekday.

1.4 Javadoc

Good programming means extensive comments and documentation. At the very


least, explain the method of each instance variable, and for each method explain
its purpose, parameters, returns, where applicable. You should also strive for a
consistent layout and for expressive variable names.
A program that is well-documented is much more valuable than the same
program without the documentation. Java comes with a tool called javadoc that
can make it easier to produce the documentation is a readable and organized
format. JavaDoc is a program that will automatically extract/generate an HTML
help-page from code that is properly commented. In particular, it is designed
produce a help file that, for a class, lists the methods, constructors and public
fields, and for each method explains what it does together with pre-conditions,

29
. Introduction to Objects

post-conditions, the meaning of the parameters, exceptions that may be thrown


and other things.
Javadoc is especially useful for documenting classes and packages of classes
that are meant to be used by other programmers. A programmer who wants to
use pre-written classes shouldn’t need to search through the source code to find
out how to use them. If the documentation in the source code is in the correct
format, javadoc can separate out the documentation and make it into a set of
web pages. The web pages are automatically formatted and linked into an easily
browseable Web site. Sun Microsystem’s documentation for the standard Java
API was produced using javadoc.
Javadoc documentation is prepared from special comments that are placed in
the Java source code file. Recall that one type of Java comment begins with /*
and ends with */. A Javadoc comment takes the same form, but it begins with
/** rather than simply /*.
/**
* This method prints a 3N+1 sequence to standard output , using
* startingValue as the initial value of N. It also prints the
number
* of terms in the sequence. The value of the parameter ,
startingValue ,
* must be a positive integer.
*/

static void print3NSequence(int startingValue) { ...

You can have Javadoc comments for methods, member variables, and for
classes. The Javadoc comment always immediately precedes the thing it is com-
menting on. Like any comment, a Javadoc comment is ignored by the computer
when the file is compiled. But there is a tool called javadoc that reads Java
source code files, extracts any Javadoc comments that it finds, and creates a set
of Web pages containing the comments in a nicely formatted, interlinked form.
By default, javadoc will only collect information about public classes, methods,
and member variables, but it allows the option of creating documentation for
non-public things as well. If javadoc doesn’t find any Javadoc comment for
something, it will construct one, but the comment will contain only basic infor-
mation such as the name and type of a member variable or the name, return type,
and parameter list of a method. This is syntactic information. To add informa-
tion about semantics and pragmatics, you have to write a Javadoc comment.
In addition to normal text, the comment can contain certain special codes.
For one thing, the comment can contain HTML mark-up commands. (HTML
is the language that is used to create web pages, and Javadoc comments are meant
to be shown on web pages.) The javadoc tool will copy any HTML commands
in the comments to the web pages that it creates. As an example, you can add <p>

30
.. Javadoc

to indicate the start of a new paragraph. (Generally, in the absence of HTML


commands, blank lines and extra spaces in the comment are ignored.)
In addition to HTML commands, Javadoc comments can include doc tags,
which are processed as commands by the javadoc tool. A doc tag has a name
that begins with the character . I will only discuss three tags: @param, @return,
and @throws. These tags are used in Javadoc comments for methods to provide
information about its parameters, its return value, and the exceptions that it
might throw. These tags are always placed at the end of the comment, after any
description of the method itself. The syntax for using them is:
@param parameter-name description-of-parameter

@return description-of-return-value

@throws exception-class-name description-of-exception

The descriptions can extend over several lines. The description ends at the
next tag or at the end of the comment. You can include a @param tag for every
parameter of the method and a @throws for as many types of exception as you
want to document. You should have a @return tag only for a non-void method.
These tags do not have to be given in any particular order. Here is an example
that doesn’t do anything exciting but that does use all three types of doc tag:
If you want to create Web-page documentation, you need to run the javadoc
tool. You can use javadoc in a command line interface similarly to the way
that the javac and java commands are used. Javadoc can also be applied in the
Eclipse integrated development environment: Just right-click the class or pack-
age that you want to document in the Package Explorer, select ”Export,” and
select ”Javadoc” in the window that pops up. Consult the documentation for
more details.

31
. Introduction to Objects

/**
* This method computes the area of a rectangle , given its width
* and its height. The length and the width should be positive
numbers.
* @param width the length of one side of the rectangle
* @param height the length the second side of the rectangle
* @return the area of the rectangle
* @throws IllegalArgumentException if either the width or the
height
* is a negative number.
*/
public static double areaOfRectangle( double length, double
width ) {
if ( width < 0 || height < 0 )
throw new IllegalArgumentException("Sides must have
positive length.");
double area;
area = width * height;
return area;
}

1.5 Creating Jar Files

As the final topic for this chapter, we look again at jar files. Recall that a jar file is a
“java archive” that can contain a number of class files. When creating a program
that uses more than one class, it’s usually a good idea to place all the classes that
are required by the program into a jar file, since then a user will only need that
one file to run the program. Jar files can also be used for stand-alone applications.
In fact, it is possible to make a so-called executable jar file. A user can run
an executable jar file in much the same way as any other application, usually by
double-clicking the icon of the jar file. (The user’s computer must have a correct
version of Java installed, and the computer must be configured correctly for this
to work. The configuration is usually done automatically when Java is installed,
at least on Windows and Mac OS.)
The question, then, is how to create a jar file. The answer depends on what
programming environment you are using. There are two basic types of program-
ming environment – command line and IDE. Any IDE (Integrated Program-
ming Environment) for Java should have a command for creating jar files. In
the Eclipse IDE, for example, it’s done as follows: In the Package Explorer pane,
select the programming project (or just all the individual source code files that
you need). Right-click on the selection, and choose “Export” from the menu
that pops up. In the window that appears, select “JAR file” and click “Next”. In
the window that appears next, enter a name for the jar file in the box labeled

32
.. Creating Jar Files

“JAR file”. (Click the “Browse” button next to this box to select the file name
using a file dialog box.) The name of the file should end with “.jar”. If you are
creating a regular jar file, not an executable one, you can hit “Finish” at this point,
and the jar file will be created. You could do this, for example, if the jar file con-
tains an applet but no main program. To create an executable file, hit the “Next”
button twice to get to the “Jar Manifest Specification” screen. At the bottom of
this screen is an input box labeled “Main class”. You have to enter the name of
the class that contains the main() method that will be run when the jar file is
executed. If you hit the “Browse” button next to the “Main class” box, you can
select the class from a list of classes that contain main() methods. Once you’ve
selected the main class, you can click the “Finish” button to create the executable
jar file.
It is also possible to create jar files on the command line. The Java Develop-
ment Kit includes a command-line program named jar that can be used to create
jar files. If all your classes are in the default package (like the examples in this
book), then the jar command is easy to use. To create a non-executable jar file
on the command line, change to the directory that contains the class files that you
want to include in the jar. Then give the command jar cf JarFileName.jar
*.class where JarFileName can be any name that you want to use for the jar file.
The “*“ in “*.class” is a wildcard that makes *.class match every class file in
the current directory. This means that all the class files in the directory will be
included in the jar file. If you want to include only certain class files, you can
name them individually, separated by spaces. (Things get more complicated if
your classes are not in the default package. In that case, the class files must be in
subdirectories of the directory in which you issue the jar file.)
Making an executable jar file on the command line is a little more compli-
cated. There has to be some way of specifying which class contains the main()
method. This is done by creating a manifest file. The manifest file can be a
plain text file containing a single line of the form Main-Class: ClassName where
ClassName should be replaced by the name of the class that contains the main()
method. For example, if the main() method is in the class MosaicDrawFrame, then
the manifest file should read “Main-Class: MosaicDrawFrame”. You can give the
manifest file any name you like. Put it in the same directory where you will issue
the jar command, and use a command of the form jar cmf ManifestFileName
JarFileName.jar *.class to create the jar file. (The jar command is capable of
performing a variety of different operations. The first parameter to the command,
such as “cf” or “cmf”, tells it which operation to perform.)
By the way, if you have successfully created an executable jar file, you can run
it on the command line using the command “java -jar”. For example:
java -jar JarFileName.jar

33
. Introduction to Objects

APIs and Packages

One of the important advantages of object-oriented programming is that it pro-


motes reuse. When writing any piece of software, a programmer can use a large
and growing body of pre-written software. The Java SDK (software develop-
ment kit) consists of thousands of classes that can be used by programmers. So,
learning the Java language means also being able to use this vast library of classes.

Toolboxes

Someone who wants to program for Macintosh computers – and to produce


programs that look and behave the way users expect them to – must deal with the
Macintosh Toolbox, a collection of well over a thousand different methods. There
are methods for opening and closing windows, for drawing geometric figures
and text to windows, for adding buttons to windows, and for responding to
mouse clicks on the window. There are other methods for creating menus and
for reacting to user selections from menus. Aside from the user interface, there are
methods for opening files and reading data from them, for communicating over
a network, for sending output to a printer, for handling communication between
programs, and in general for doing all the standard things that a computer has
to do. Microsoft Windows provides its own set of methods for programmers to
use, and they are quite a bit different from the methods used on the Mac. Linux
has several different GUI toolboxes for the programmer to choose from.
The analogy of a “toolbox” is a good one to keep in mind. Every program-
ming project involves a mixture of innovation and reuse of existing tools. A pro-
grammer is given a set of tools to work with, starting with the set of basic tools
that are built into the language: things like variables, assignment statements, if
statements, and loops. To these, the programmer can add existing toolboxes full
of methods that have already been written for performing certain tasks. These
tools, if they are well-designed, can be used as true black boxes: They can be
called to perform their assigned tasks without worrying about the particular steps
they go through to accomplish those tasks. The innovative part of programming
is to take all these tools and apply them to some particular project or problem
(word-processing, keeping track of bank accounts, processing image data from
a space probe, Web browsing, computer games,...). This is called applications
programming.
A software toolbox is a kind of black box, and it presents a certain interface
to the programmer. This interface is a specification of what methods are in the
toolbox, what parameters they use, and what tasks they perform. This informa-
tion constitutes the API, or Applications Programming Interface, associated with
the toolbox. The Macintosh API is a specification of all the methods available
in the Macintosh Toolbox. A company that makes some hardware device – say

34
.. Creating Jar Files

a card for connecting a computer to a network – might publish an API for that
device consisting of a list of methods that programmers can call in order to com-
municate with and control the device. Scientists who write a set of methods for
doing some kind of complex computation – such as solving “differential equa-
tions”, say – would provide an API to allow others to use those methods without
understanding the details of the computations they perform.
The Java programming language is supplemented by a large, standard API.
You’ve seen part of this API already, in the form of mathematical methods such as
Math.sqrt(), the String data type and its associated methods, and the
System.out.print() methods. The standard Java API includes methods for
working with graphical user interfaces, for network communication, for read-
ing and writing files, and more. It’s tempting to think of these methods as being
built into the Java language, but they are technically methods that have been
written and made available for use in Java programs.
Java is platform-independent. That is, the same program can run on plat-
forms as diverse as Macintosh, Windows, Linux, and others. The same Java
API must work on all these platforms. But notice that it is the interface that is
platform-independent; the implementation varies from one platform to another.
A Java system on a particular computer includes implementations of all the stan-
dard API methods. A Java program includes only calls to those methods. When
the Java interpreter executes a program and encounters a call to one of the stan-
dard methods, it will pull up and execute the implementation of that method
which is appropriate for the particular platform on which it is running. This is a
very powerful idea. It means that you only need to learn one API to program for
a wide variety of platforms.

Javaʼs Standard Packages

Like all methods in Java, the methods in the standard API are grouped into
classes. To provide larger-scale organization, classes in Java can be grouped into
packages, which were introduced briefly in Subection2.6.4. You can have even
higher levels of grouping, since packages can also contain other packages. In fact,
the entire standard Java API is implemented in several packages. One of these,
which is named “java”, contains several non-GUI packages as well as the original
AWT graphics user interface classes. Another package, “javax”, was added in Java
version 1.2 and contains the classes used by the Swing graphical user interface
and other additions to the API.
A package can contain both classes and other packages. A package that is
contained in another package is sometimes called a “sub-package.” Both the java
package and the javax package contain sub-packages. One of the sub-packages of
java, for example, is called “awt”. Since awt is contained within java, its full name
is actually java.awt. This package contains classes that represent GUI components

35
. Introduction to Objects

such as buttons and menus in the AWT, the older of the two Java GUI toolboxes,
which is no longer widely used. However, java.awt also contains a number of
classes that form the foundation for all GUI programming, such as the Graphics
class which provides methods for drawing on the screen, the Color class which
represents colors, and the Font class which represents the fonts that are used to
display characters on the screen. Since these classes are contained in the pack-
age java.awt, their full names are actually java.awt.Graphics, java.awt.Color
and java.awt.Font. (I hope that by now you’ve gotten the hang of how this
naming thing works in Java.) Similarly, javax contains a sub-package named
javax.swing, which includes such classes as javax.swing.JButton, javax.swing.JMenu,
and javax.swing.JFrame. The GUI classes in javax.swing, together with the
foundational classes in java.awt are all part of the API that makes it possible to
program graphical user interfaces in Java.
The java package includes several other sub-packages, such as java.io, which
provides facilities for input/output, java.net, which deals with network com-
munication, and java.util, which provides a variety of “utility” classes. The most
basic package is called java.lang. This package contains fundamental classes
such as String, Math, Integer, and Double.
It might be helpful to look at a graphical representation of the levels of nesting
in the java package, its sub-packages, the classes in those sub-packages, and the
methods in those classes. This is not a complete picture, since it shows only a
very few of the many items in each element:

The official documentation for the standard Java 5.0 API lists 165 differ-
ent packages, including sub-packages, and it lists 3278 classes in these packages.
Many of these are rather obscure or very specialized, but you might want to
browse through the documentation to see what is available.

36
.. Creating Jar Files

Even an expert programmer won’t be familiar with the entire API, or even
a majority of it. In this book, you’ll only encounter several dozen classes, and
those will be sufficient for writing a wide variety of programs.

Using Classes from Packages

Let’s say that you want to use the class java.awt.Color in a program that you
are writing. Like any class, java.awt.Color is a type, which means that you can
use it declare variables and parameters and to specify the return type of a method.
One way to do this is to use the full name of the class as the name of the type. For
example, suppose that you want to declare a variable named rectColor of type
java.awt.Color. You could say:

java.awt.Color rectColor;

This is just an ordinary variable declaration of the form “type-name variable-


name;”. Of course, using the full name of every class can get tiresome, so Java
makes it possible to avoid using the full names of a class by importing the class.
If you put
import java.awt.Color;

at the beginning of a Java source code file, then, in the rest of the file, you
can abbreviate the full name java.awt.Color to just the simple name of the class,
Color. Note that the import line comes at the start of a file and is not inside any
class. Although it is sometimes referred to as as a statement, it is more properly
called an import directive since it is not a statement in the usual sense. Using
this import directive would allow you to say
Color rectColor;

to declare the variable. Note that the only effect of the import directive is to allow
you to use simple class names instead of full “package.class” names; you aren’t
really importing anything substantial. If you leave out the import directive, you
can still access the class – you just have to use its full name. There is a shortcut
for importing all the classes from a given package. You can import all the classes
from java.awt by saying
import java.awt.*;

The “*” is a wildcard that matches every class in the package. (However, it does
not match sub-packages; you cannot import the entire contents of all the sub-
packages of the java packages by saying importjava.*.)
Some programmers think that using a wildcard in an import statement is bad
style, since it can make a large number of class names available that you are not
going to use and might not even know about. They think it is better to explicitly
import each individual class that you want to use. In my own programming,

37
. Introduction to Objects

I often use wildcards to import all the classes from the most relevant packages,
and use individual imports when I am using just one or two classes from a given
package.
In fact, any Java program that uses a graphical user interface is likely to use
many classes from the java.awt and java.swing packages as well as from another
package named java.awt.event, and I usually begin such programs with
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

A program that works with networking might include: “import java.net.*;”,


while one that reads or writes files might use “import java.io.*;”. (But when
you start importing lots of packages in this way, you have to be careful about
one thing: It’s possible for two classes that are in different packages to have the
same name. For example, both the java.awt package and the java.util package
contain classes named List. If you import both java.awt.* and java.util.*,
the simple name List will be ambiguous. If you try to declare a variable of
type List, you will get a compiler error message about an ambiguous class name.
The solution is simple: use the full name of the class, either java.awt.List or
java.util.List. Another solution, of course, is to use import to import the
individual classes you need, instead of importing entire packages.)
Because the package java.lang is so fundamental, all the classes in java.lang
are automatically imported into every program. It’s as if every program began
with the statement “import java.lang.*;”. This is why we have been able to use
the class name String instead of java.lang.String, and Math.sqrt() instead of
java.lang.Math.sqrt(). It would still, however, be perfectly legal to use the
longer forms of the names.
Programmers can create new packages. Suppose that you want some classes
that you are writing to be in a package named utilities. Then the source code file
that defines those classes must begin with the line
package utilities;

This would come even before any import directive in that file. Furthermore,
the source code file would be placed in a folder with the same name as the package.
A class that is in a package automatically has access to other classes in the same
package; that is, a class doesn’t have to import the package in which it is defined.
In projects that define large numbers of classes, it makes sense to organize
those classes into packages. It also makes sense for programmers to create new
packages as toolboxes that provide functionality and API’s for dealing with areas
not covered in the standard Java API. (And in fact such “toolmaking” program-
mers often have more prestige than the applications programmers who use their
tools.)

38
.. Mixing Static and Non-static

However, I will not be creating any packages in this textbook. For the pur-
poses of this book, you need to know about packages mainly so that you will
be able to import the standard packages. These packages are always available to
the programs that you write. You might wonder where the standard classes are
actually located. Again, that can depend to some extent on the version of Java
that you are using, but in the standard Java 5.0, they are stored in jar files in
a subdirectory of the main Java installation directory. A jar (or “Java archive”)
file is a single file that can contain many classes. Most of the standard classes
can be found in a jar file named classes.jar. In fact, Java programs are generally
distributed in the form of jar files, instead of as individual class files.
Although we won’t be creating packages explicitly, every class is actually part
of a package. If a class is not specifically placed in a package, then it is put in
something called the default package, which has no name. All the examples that
you see in this book are in the default package.

1.6 Mixing Static and Non-static

Classes, as I’ve said, have two very distinct purposes. A class can be used to group
together a set of static member variables and static member methods. Or it can
be used as a factory for making objects. The non-static variables and methods in
the class definition specify the instance variables and methods of the objects. In
most cases, a class performs one or the other of these roles, not both.
Sometimes, however, static and non-static members are mixed in a single class.
In this case, the class plays a dual role. Sometimes, these roles are completely
separate. It is also possible for the static and non-static parts of a class to interact.
This happens when instance methods use static member variables or call static
member methods. An instance method belongs to an object, not to the class itself,
and there can be many objects with their own versions of the instance method.
But there is only one copy of a static member variable. So, effectively, we have
many objects sharing that one variable.
Suppose, for example, that we want to write a PairOfDice class that uses the
Random class for rolling the dice. To do this, a PairOfDice object needs access
to an object of type Random. But there is no need for each PairOfDice object to
have a separate Randomobject. (In fact, it would not even be a good idea: Because
of the way ran dom number generators work, a program should, in general, use
only one source of random numbers.) A nice solution is to have a single Random
variable as a static member of the PairOfDice class, so that it can be shared by
all PairOfDice objects. For example:
import java.util.Random;

public class PairOfDice {

39
. Introduction to Objects

\code{private static Random randGen = new Random();}

public int die1; // Number showing on the first die.


public int die2; // Number showing on the second die.

public PairOfDice() {
// Constructor. Creates a pair of dice that
// initially shows random values.
roll();
}
public void roll() {
// Roll the dice by setting each of the dice to be
// a random number between 1 and 6.
die1 = randGen.nextInt(6) + 1;
die2 = randGen.nextInt(6) + 1;
}
} // end class PairOfDice

As another example, let’s rewrite the Student class. I’ve added an ID for each
student and a static member called nextUniqueID . Although there is an ID
variable in each student object, there is only one nextUniqueID variable.
public class Student {

private String name; // Student 's name.


private int ID; // Unique ID number for this student.
public double test1, test2, test3; // Grades on three tests.

private static int nextUniqueID = 0;


// keep track of next available unique ID number

Student(String theName) {
// Constructor for Student objects; provides a name for
the Student ,
// and assigns the student a unique ID number.
name = theName;
nextUniqueID++;
ID = nextUniqueID;
}
public String getName() {
// Accessor method for reading value of private
// instance variable , name.
return name;
}
public int getID() {
// Accessor method for reading value of ID.

40
.. Mixing Static and Non-static

return ID;
}
public double getAverage() {
// Compute average test grade.
return (test1 + test2 + test3) / 3;
}
} // end of class Student

The initialization “nextUniqueID = 0” is done once, when the class is first


loaded. Whenever a Student object is constructed and the constructor says
“nextUniqueID++;”, it’s always the same static member variable that is being
incremented. When the very first Student object is created, nextUniqueID be-
comes 1. When the second object is created, nextUniqueID becomes 2. After the
third object, it becomes 3. And so on. The constructor stores the new value of
nextUniqueID in the ID variable of the object that is being created. Of course,
ID is an instance variable, so every object has its own individual ID variable. The
class is constructed so that each student will automatically get a different value
for its IDvariable. Furthermore, the ID variable is private, so there is no way
for this variable to be tampered with after the object has been created. You are
guaranteed, just by the way the class is designed, that every student object will
have its own permanent, unique identification number. Which is kind of cool if
you think about it.

Static Import

The import directive makes it possible to refer to a class such as java.awt.Color


using its simple name, Color. All you have to do is say import java.awt.Color
or import java.awt.*. Uou still have to use compound names to refer to static
member variables such as System.out and to static methods such as Math.sqrt.
Java 5.0 introduced a new form of the import directive that can be used to
import static members of a class in the same way that the ordinary import
directive imports classes from a package. The new form of the directive is called
a static import, and it has syntax
import static package-name class-name static-member-name;

to import one static member name from a class, or


import static package-name class-name.*;

to import all the public static members from a class. For example, if you preface
a class definition with
import static java.lang.System.out;

then you can use the simple name out instead of the compound name System.out.
This means you can use out.println instead of System.out.println. If you are

41
. Introduction to Objects

going to work extensively with the Mathclass, you can preface your class definition
with
import static java.lang.Math.*;

This would allow to say sqrtinstead of Math.sqrt, log instead of Math.log,


PI instead of Math.PI, and so on.
Note that the static import directive requires a package-name, even for classes
in the standard package java.lang. One consequence of this is that you can’t do
a static import from a class in the default package.

42
Chapter

2
Object Oriented
Analysis and Design

2.1 Software Engineering

he difficulties inherent with the development of software has led many


T computer scientists to suggest that software development should be treated
as an engineering activity. They argue for a disciplined approach where the soft-
ware engineer uses carefully thought out methods and processes.
Large, complex programming projects are only likely to succeed if a careful,
systematic approach is adopted during all stages of the software life cycle. The
systematic approach to programming, using accepted principles of good design,
is called software engineering. The software engineer tries to efficiently construct
programs that verifyably meet their specifications and that are easy to modify
if necessary. There is a wide range of “methodologies” that can be applied to
help in the systematic design of programs. (Most of these methodologies seem
to involve drawing little boxes to represent program components, with labeled
arrows to represent relationships among the boxes.)
The term software engineering has several meanings (from wikipedia):

† As the broad term for all aspects of the practice of computer programming,
as opposed to the theory of computer programming, which is called com-
puter science;

† As the term embodying the advocacy of a specific approach to computer


programming, one that urges that it be treated as an engineering profes-
sion rather than an art or a craft, and advocates the codification of recom-
mended practices in the form of software engineering methodologies.

43
. Object Oriented Analysis and Design

† Software engineering is

(1) “the application of a systematic, disciplined, quantifiable approach to


the development, operation, and maintenance of software, that is, the
application of engineering to software,” and
(2) “the study of approaches as in (1).” IEEE Standard 610.12

Software Engineering Life-Cycles

A decades-long goal has been to find repeatable, predictable processes or method-


ologies that improve productivity and quality of software. Some try to system-
atize or formalize the seemingly unruly task of writing software. Others apply
project management techniques to writing software. Without project manage-
ment, software projects can easily be delivered late or over budget. With large
numbers of software projects not meeting their expectations in terms of function-
ality, cost, or delivery schedule, effective project management is proving difficult.
A large programming project goes through a number of stages, starting with
specification of the problem to be solved, followed by analysis of the problem
and design of a program to solve it. Then comes coding, in which the program’s
design is expressed in some actual programming language. This is followed by
testing and debugging of the program. After that comes a long period of mainte-
nance, which means fixing any new problems that are found in the program and
modifying it to adapt it to changing requirements. Together, these stages form
what is called the software life cycle. (In the real world, the ideal of consecutive
stages is seldom if ever achieved. During the analysis stage, it might turn out
that the specifications are incomplete or inconsistent. A problem found during
testing requires at least a brief return to the coding stage. If the problem is seri-
ous enough, it might even require a new design. Maintenance usually involves
redoing some of the work from previous stages.)
Software engineering requires performing many tasks, notably the following,
some of which may not seem to directly produce software.
• Requirements Analysis Extracting the requirements of a desired software
product is the first task in creating it. While customers probably believe
they know what the software is to do, it may require skill and experience in
software engineering to recognize incomplete, ambiguous or contradictory
requirements.
• Specification Specification is the task of precisely describing the software
to be written, usually in a mathematically rigorous way. In reality, most
successful specifications are written to understand and fine-tune applica-
tions that were already well-developed. Specifications are most important
for external interfaces, that must remain stable.

44
.. Software Engineering

• Design and Architecture Design and architecture refer to determining how


software is to function in a general way without being involved in details.
Usually this phase is divided into two sub-phases.

• Coding Reducing a design to code may be the most obvious part of the
software engineering job, but it is not necessarily the largest portion.

• Testing Testing of parts of software, especially where code by two different


engineers must work together, falls to the software engineer.

• Documentation An important (and often overlooked) task is document-


ing the internal design of software for the purpose of future maintenance
and enhancement. Documentation is most important for external inter-
faces.

• Maintenance Maintaining and enhancing software to cope with newly dis-


covered problems or new requirements can take far more time than the
initial development of the software. Not only may it be necessary to add
code that does not fit the original design but just determining how software
works at some point after it is completed may require significant effort by
a software engineer. About 2/3 of all software engineering work is main-
tenance, but this statistic can be misleading. A small part of that is fixing
bugs. Most maintenance is extending systems to do new things, which in
many ways can be considered new work. In comparison, about 2/3 of all
civil engineering, architecture, and construction work is maintenance in a
similar way.

Object Oriented Software Engineering

Object Oriented Software Engineering rests on three principles:

• Abstraction: Ignore the details. In philosophical terminology, abstraction


is the thought process wherein ideas are distanced from objects. In com-
puter science, abstraction is a mechanism and practice to reduce and factor
out details so that one can focus on few concepts at a time.
Abstraction uses a strategy of simplification, wherein formerly concrete de-
tails are left ambiguous, vague, or undefined. [wikipedia:Abstraction]

• Modularization: Break into pieces. A module can be defined variously,


but generally must be a component of a larger system, and operate within
that system independently from the operations of the other components.
Modularity is the property of computer programs that measures the extent
to which they have been composed out of separate parts called modules.

45
. Object Oriented Analysis and Design

Programs that have many direct interrelationships between any two ran-
dom parts of the program code are less modular than programs where those
relationships occur mainly at well-defined interfaces between modules.

• Information hiding: separate the implementation and the function. The


principle of information hiding is the hiding of design decisions in a com-
puter program that are most likely to change, thus protecting other parts
of the program from change if the design decision is changed. Protecting
a design decision involves providing a stable interface which shields the
remainder of the program from the implementation (the details that are
most likely to change).

Abstraction and Information Hiding

Methods as Abstraction Mechanisms

A good example of abstraction and information hiding in practice is the method.


A method is an abstraction mechanism and consists of instructions for per-
forming some task, chunked together and given a name. “Chunking” allows
you to deal with a potentially very complicated task as a single concept. In-
stead of worrying about the many, many steps that the computer might have to
go through to perform that task, you just need to remember the name of the
method. Whenever you want your program to perform the task, you just call
the method. Methods are a major tool for dealing with complexity.
A method is a “black box” because you do not have to see what’s “inside” it
before you use it–the complexity of how it works is hidden. A black box needs
some kind of interface with the rest of the world, which allows some interaction
between what’s inside the box and what’s outside. A physical black box might
have buttons on the outside that you can push, dials that you can set, and slots
that can be used for passing information back and forth. Since we are trying to
hide complexity, not create it, we have the first rule of black boxes:

The interface of a black box should be fairly straightforward, well-defined,


and easy to understand.

Your television, your car, your VCR, your refrigerator... are all examples of
black boxes in the real world. You can turn your television on and off, change
channels, and set the volume by using elements of the television’s interface –
dials and remote control – without understanding anything about how the thing
actually works.
A black box does have an inside – the code in a method that actually performs
the task, all the electronics inside your television set. The inside of a black box is
called its implementation. The second rule of black boxes is that:

46
.. Software Engineering

To use a black box, you shouldn’t need to know anything about its im-
plementation; all you need to know is its interface.

In fact, it should be possible to change the implementation, as long as the be-


havior of the box, as seen from the outside, remains unchanged. For example,
when the insides of TV sets went from using vacuum tubes to using transistors,
the users of the sets didn’t even need to know about it – or even know what it
means. Similarly, it should be possible to rewrite the inside of a method, to use
more efficient code, for example, without affecting the programs that use that
method.
The implementor of the black box doesn’t need to know about the many ways
in which the box will be used–she just needs to make sure that the box performs
its assigned task and interfaces correctly with the rest of the world. This is the
third rule of black boxes:

The implementor of a black box should not need to know anything about
the larger systems in which the box will be used. In a way, a black box
divides the world into two parts: the inside (implementation) and the
outside. The interface is at the boundary, connecting those two parts.

You should not think of an interface as just the physical connection between the
box and the rest of the world. The interface also includes a specification of what
the box does and how it can be controlled by using the elements of the physical
interface. It’s not enough to say that a TV set has a power switch; you need to
specify that the power switch is used to turn the TV on and off!
The interface of a method has a semantic as well as a syntactic component.
The syntactic part of the interface tells you just what you have to type in order
to call the method. The semantic component specifies the task the method will
accomplish. To write a legal program, you need to know the syntactic specifica-
tion of the method. You need to know the method’s semantic specification to
understand the purpose of the method and to use it effectively. We refer to both
parts of the interface – syntactic and semantic – collectively as the contract of
the method.
The contract of a method says, essentially, “Here is what you have to do to use
me, and here is what I will do for you, guaranteed.” When you write a method,
the comments that you write for the method should make the contract very clear.

Preconditions and Postconditions

When working with methods as building blocks, it is important to be clear about


how a method interacts with the rest of the program. A convenient way to express
the contract of a method is in terms of preconditions and postconditions.

47
. Object Oriented Analysis and Design

The precondition of a method is something that must be true when the


method is called, if the method is to work correctly.

For example, for the built-in method Math.sqrt(x), a precondition is that the
parameter, x, is greater than or equal to zero, since it is not possible to take the
square root of a negative number. In terms of a contract, a precondition repre-
sents an obligation of the caller of the method. If you call a method without
meeting its precondition, then there is no reason to expect it to work properly.
The program might crash or give incorrect results, but you can only blame your-
self, not the method.
A postcondition of a method represents the other side of the contract. It
is something that will be true after the method has run. The postcondition of
the method Math.sqrt() is that the square of the value that is returned by this
method is equal to the parameter that is provided when the method is called.
Of course, this will only be true if the precondition – that the parameter is
greater than or equal to zero – is met. A postcondition of the built-in method
System.out.print() is that the value of the parameter has been displayed on the
screen.
Preconditions most often give restrictions on the acceptable values of param-
eters, as in the example of Math.sqrt(x). However, they can also refer to global
variables that are used in the method. The postcondition of a method specifies
the task that it performs. For a method, the postcondition should specify the
value that the method returns. When you are given a pre-written method, a
statement of its preconditions and postconditions tells you how to use it and
what it does. When you write a method, the preconditions and postconditions
give you an exact specification of what the method is expected to do. It is a good
idea to write preconditions and postconditions as comments.

Classes as Abstraction Mechanisms

Methods are not the only example of black boxes in programming. A class is
also a black box. It has a “public” part, representing its interface, and a “private”
part that is entirely inside its hidden implementation. All the principles of black
boxes apply to classes as well as to methods.
The interface to a class consists of all the members of the class that are acces-
sible. Usually, this will include all the public methods as we generally hide the
fields. When a method is made accessible, one needs only to know its signature:
i.e. the return type, name and arguments. As the user programmer, then, we
must know WHAT the method does, and not HOW it works.

48
.. A Simple Methodology

Figure .: A Simple Methodology

2.2 A Simple Methodology

The following is a simple methodology that we will use:

Discovering Classes: Class-Responsibility-Collaboration cards

The concepts of object orientation does not only apply to the coding stage of pro-
gram development. But there are also object-oriented methodologies for analysis
and design. The question in this stage of the software life cycle is, “How can

49
. Object Oriented Analysis and Design

one discover or invent the overall structure of a program?” As an example of a


rather simple object-oriented approach to analysis and design, consider this ad-
vice: Write down a description of the problem. Underline all the nouns in that
description. The nouns should be considered as candidates for becoming classes
or objects in the program design. Similarly, underline all the verbs. These are
candidates for methods. This is your starting point. Further analysis might un-
cover the need for more classes and methods, and it might reveal that subclassing
can be used to take advantage of similarities among classes.
This is perhaps a bit simple-minded, but the idea is clear and the general
approach can be effective: Analyze the problem to discover the concepts that are
involved, and create classes to represent those concepts. The design should arise
from the problem itself, and you should end up with a program whose structure
reflects the structure of the problem in a natural way.
We strive for responsibility-driven design: each class should be responsible
for its own data. We strive for loose coupling: each class is largely independent
and communicates with other classes via a small well-defined interface. We strive
for cohesion: each class performs one and only one task (for readability, reuse).
Class-Responsibility-Collaboration cards (CRC cards) are a brainstorming
tool used in the design of object-oriented software. They were proposed by Ward
Cunningham. They are typically used when first determining which classes are
needed and how they will interact.
CRC cards are usually created from index cards on which are written:
1. The class name.
2. The package name (if applicable).
3. The responsibilities of the class.
4. The names of other classes that the class will collaborate with to fulfill its
responsibilities.
For example consider the CRC Card for a Playing Card class:

The responsibilities are listed on the left. The classes that the Playing Card
class will collaborate with are listed on the right.

50
.. Documenting Designs: The Unified Modelling Language

The idea is that class design is undertaken by a team of developers. CRC


cards are used as a brainstorming technique. The team attempts to determine
all the classes and their responsibilities that will be needed for the application.
The team runs through various usage scenarios of the application. For e.g. one
such scenario for a game of cards may be “the player picks a card from the deck
and adds it to his hand”. The team uses the CRC cards to check if this scenario
can be handled by the responsibilites assigned to the classes. In this way, the
design is refined until the team agrees on a set of classes and has agreed on their
responsibilities.
Using a small card keeps the complexity of the design at a minimum. It
focuses the designer on the essentials of the class and prevents him from getting
into its details and inner workings at a time when such detail is probably counter-
productive. It also forces the designer to refrain from giving the class too many
responsibilities.

2.3 Documenting Designs: The Unified Modelling Language

his section will give you a quick overview of the basics of UML. Keep
T in mind that this is not a comprehensive tutorial on UML but rather a brief
introduction to UML which can be read as a UML tutorial. If you would like to
learn more about the Unified Modelling Language, or in general about software
analysis and design, refer to one of the many books available on the topic. There
are also a lot of tutorials on the Internet which you can take as a starting point.1
The Unified Modelling Language (UML) is a diagramming language or nota-
tion to specify, visualize and document models of Object Oriented software sys-
tems. UML is not a development method, that means it does not tell you what
to do first and what to do next or how to design your system, but it helps you to
visualize your design and communicate with others. UML is controlled by the
Object Management Group (OMG) and is the industry standard for graphically
describing software.
UML is designed for Object Oriented software design and has limited use
for other programming paradigms.
UML is not a method by itself, however it was designed to be compatible
with the leading object-oriented software development methods of its time (e.g.,
OMT, Booch, Objectory). Since UML has evolved, some of these methods have
been recast to take advantage of the new notation (e.g., OMT) and new meth-
ods have been created based on UML. Most well known is the Rational Unified
Process (RUP) created by the Rational Software Corporation.
1 This discussion is taken from the user documentation of the UML tool Umbrello and
wikipedia.

51
. Object Oriented Analysis and Design

Modelling There are three prominent parts of a system’s model:

• Functional Model
Showcases the functionality of the system from the user’s Point of View.
Includes Use Case Diagrams.

• Object Model
Showcases the structure and substructure of the system using objects, at-
tributes, operations, and associations. Includes Class Diagrams.

• Dynamic Model
Showcases the internal behavior of the system. Includes Sequence Dia-
grams, Activity Diagrams and State Machine Diagrams.

UML is composed of many model elements that represent the different parts
of a software system. The UML elements are used to create diagrams, which
represent a certain part, or a point of view of the system. In UML 2.0 there are
13 types of diagrams. Some of the more important diagrams are:

• Use Case Diagrams show actors (people or other users of the system), use
cases (the scenarios when they use the system), and their relationships

• Class Diagrams show classes and the relationships between them

• Sequence Diagrams show objects and a sequence of method calls they make
to other objects.

• Collaboration Diagrams show objects and their relationship, putting em-


phasis on the objects that participate in the message exchange

• State Diagrams show states, state changes and events in an object or a part
of the system

• Activity Diagrams show activities and the changes from one activity to an-
other with the events occurring in some part of the system

• Component Diagrams show the high level programming components (such


as KParts or Java Beans).

• Deployment Diagrams show the instances of the components and their re-
lationships.

52
.. Documenting Designs: The Unified Modelling Language

Figure .: Umbrello UML Modeller showing a Use Case Diagram

Use Case Diagrams Use Case Diagrams describe the relationships and depen-
dencies between a group of Use Cases and the Actors participating in the process.
It is important to notice that Use Case Diagrams are not suited to represent
the design, and cannot describe the internals of a system. Use Case Diagrams are
meant to facilitate the communication with the future users of the system, and
with the customer, and are specially helpful to determine the required features
the system is to have. Use Case Diagrams tell, what the system should do but do
not–and cannot–specify how this is to be achieved.
A Use Case describes–from the point of view of the actors–a group of activities
in a system that produces a concrete, tangible result.
Use Cases are descriptions of the typical interactions between the users of a
system and the system itself. They represent the external interface of the system
and specify a form of requirements of what the system has to do (remember, only
what, not how).
When working with Use Cases, it is important to remember some simple
rules:

• Each Use Case is related to at least one actor

53
. Object Oriented Analysis and Design

• Each Use Case has an initiator (i.e. an actor)

• Each Use Case leads to a relevant result (a result with a business value)

An actor is an external entity (outside of the system) that interacts with the
system by participating (and often initiating) a Use Case. Actors can be in real
life people (for example users of the system), other computer systems or external
events.
Actors do not represent the physical people or systems, but their role . This
means that when a person interacts with the system in different ways (assuming
different roles) he will be represented by several actors. For example a person that
gives customer support by the telephone and takes orders from the customer into
the system would be represented by an actor “Support Staff” and an actor “Sales
Representative”
U se Case Descriptions are textual narratives of the Use Case. They usually
take the form of a note or a document that is somehow linked to the Use Case,
and explains the processes or activities that take place in the Use Case.

Class Diagrams Class Diagrams show the different classes that make up a system
and how they relate to each other. Class Diagrams are said to be ”static” diagrams
because they show the classes, along with their methods and attributes as well as
the static relationships between them: which classes ”know” about which classes
or which classes ”are part” of another class, but do not show the method calls
between them.
A Class defines the attributes and the methods of a set of objects. All objects
of this class (instances of this class) share the same behavior, and have the same
set of attributes (each object has its own set). The term ”Type” is sometimes used
instead of Class, but it is important to mention that these two are not the same,
and Type is a more general term.
In UML, Classes are represented by rectangles, with the name of the class,
and can also show the attributes and operations of the class in two other ”com-
partments” inside the rectangle.
In UML, Attributes are shown with at least their name, and can also show
their type, initial value and other properties. Attributes can also be displayed
with their visibility:

• + Stands for public attributes

• # Stands for protected attributes

• - Stands for private attributes

54
.. Documenting Designs: The Unified Modelling Language

Figure .: Umbrello UML Modeller showing a Class Diagram

Figure .: Visual representation of a Class in UML

55
. Object Oriented Analysis and Design

Figure .: Visual representation of a generalization in UML

Operations (methods) are also displayed with at least their name, and can
also show their parameters and return types. Operations can, just as Attributes,
display their visibility:
• + Stands for public operations
• # Stands for protected operations
• - Stands for private operations

Class Associations Classes can relate (be associated with) to each other in dif-
ferent ways:
Inheritance is one of the fundamental concepts of Object Orientated pro-
gramming, in which a class “gains” all of the attributes and operations of the
class it inherits from, and can override/modify some of them, as well as add more
attributes and operations of its own.

• Generalization In UML, a Generalization association between two classes


puts them in a hierarchy representing the concept of inheritance of a de-
rived class from a base class. In UML, Generalizations are represented by a
line connecting the two classes, with an arrow on the side of the base class.
• Association An association represents a relationship between classes, and
gives the common semantics and structure for many types of ”connections”
between objects.
Associations are the mechanism that allows objects to communicate to each
other. It describes the connection between different classes (the connection
between the actual objects is called object connection, or link .
Associations can have a role that specifies the purpose of the association
and can be uni- or bidirectional (indicates if the two objects participating
in the relationship can send messages to the other, of if only one of them

56
.. Documenting Designs: The Unified Modelling Language

Figure .: Visual representation of an Association in UML Aggregation

Figure .: Visual representation of an Aggregation relationship in UML

knows about the other). Each end of the association also has a multiplicity
value, which dictates how many objects on this side of the association can
relate to one object on the other side.
In UML, associations are represented as lines connecting the classes partic-
ipating in the relationship, and can also show the role and the multiplicity
of each of the participants. Multiplicity is displayed as a range [min..max]
of non-negative values, with a star (*) on the maximum side representing
infinite.
• Aggregations Aggregations are a special type of associations in which the
two participating classes don’t have an equal status, but make a “whole-
part” relationship. An Aggregation describes how the class that takes the
role of the whole, is composed (has) of other classes, which take the role
of the parts. For Aggregations, the class acting as the whole always has a
multiplicity of one.
In UML, Aggregations are represented by an association that shows a rhom-
bus on the side of the whole.
• Composition Compositions are associations that represent very strong ag-
gregations. This means, Compositions form whole-part relationships as
well, but the relationship is so strong that the parts cannot exist on its own.
They exist only inside the whole, and if the whole is destroyed the parts die
too.
In UML, Compositions are represented by a solid rhomb on the side of
the whole.
Other Class Diagram Items Class diagrams can contain several other items
besides classes.
• Interfaces are abstract classes which means instances can not be directly
created of them. They can contain operations but no attributes. Classes

57
. Object Oriented Analysis and Design

Figure .

can inherit from interfaces (through a realisation association) and instances


can then be made of these diagrams.
• Datatypes are primitives which are typically built into a programming lan-
guage. Common examples include integers and booleans. They can not
have relationships to classes but classes can have relationships to them.
• Enums are a simple list of values. A typical example is an enum for days of
the week. The options of an enum are called Enum Literals. Like datatypes
they can not have relationships to classes but classes can have relationships
to them.
• Packages represent a namespace in a programming language. In a diagram
they are used to represent parts of a system which contain more than one
class, maybe hundereds of classes.

Sequence Diagrams Sequence Diagrams show the message exchange (i.e. method
call) between several Objects in a specific time-delimited situation. Objects are
instances of classes. Sequence Diagrams put special emphasis in the order and
the times in which the messages to the objects are sent.
In Sequence Diagrams objects are represented through vertical dashed lines,
with the name of the Object on the top. The time axis is also vertical, increasing
downwards, so that messages are sent from one Object to another in the form of
arrows with the operation and parameters name.
Messages can be either synchronous, the normal type of message call where
control is passed to the called object until that method has finished running, or
asynchronous where control is passed back directly to the calling object. Syn-
chronous messages have a vertical box on the side of the called object to show
the flow of program control.

Collaboration Diagrams Collaboration Diagrams show the interactions occur-


ring between the objects participating in a specific situation. This is more or less
the same information shown by Sequence Diagrams but there the emphasis is
put on how the interactions occur in time while the Collaboration Diagrams put
the relationships between the objects and their topology in the foreground.
In Collaboration Diagrams messages sent from one object to another are rep-
resented by arrows, showing the message name, parameters, and the sequence of

58
.. Documenting Designs: The Unified Modelling Language

Figure .: Umbrello UML Modeller showing a Sequence Diagram

the message. Collaboration Diagrams are specially well suited to showing a spe-
cific program flow or situation and are one of the best diagram types to quickly
demonstrate or explain one process in the program logic.

State Diagram State Diagrams show the different states of an Object during its
life and the stimuli that cause the Object to change its state.
State Diagrams view Objects as state machines or finite automata that can be
in one of a set of finite states and that can change its state via one of a finite set
of stimuli. For example an Object of type NetServer can be in one of following
states during its life:
• Ready
• Listening
• Working
• Stopped
and the events that can cause the Object to change states are
• Object is created
• Object receives message listen

59
. Object Oriented Analysis and Design

Figure .: Umbrello UML Modeller showing a Collaboration Diagram

• A Client requests a connection over the network


• A Client terminates a request
• The request is executed and terminated
• Object receives message stop ... etc

Activity Diagram Activity Diagrams describe the sequence of activities in a sys-


tem with the help of Activities. Activity Diagrams are a special form of State
Diagrams, that only (or mostly) contains Activities.
Activity Diagrams are always associated to a Class , an Operation or a Use Case
.
Activity Diagrams support sequential as well as parallel Activities. Parallel
execution is represented via Fork/Wait icons, and for the Activities running in
parallel, it is not important the order in which they are carried out (they can be
executed at the same time or one after the other)

60
.. Documenting Designs: The Unified Modelling Language

Figure .: Umbrello UML Modeller showing a State Diagram

Figure .: Umbrello UML Modeller showing an Activity Diagram

61
. Object Oriented Analysis and Design

2.4 A Case Study: Card Games

n this section, we look at some specific examples of object-oriented design


I in a domain that is simple enough that we have a chance of coming up with
something reasonably reusable. Consider card games that are played with a stan-
dard deck of playing cards (a so-called “poker” deck, since it is used in the game
of poker).

Designing the classes

When designing object-oriented software, a crucial first step is to identify the


objects that will make up the application. One approach to do this is to identify
the nouns in the problem description. These become candidates for objects. Next
we can identify verbs in the description: these suggest methods for the objects.
Consider the following description of a card game:

In a typical card game, each player gets a hand of cards. The deck is
shuffled and cards are dealt one at a time from the deck and added to
the players’ hands. In some games, cards can be removed from a hand,
and new cards can be added. The game is won or lost depending on
the value (ace, 2, ..., king) and suit (spades, diamonds, clubs, hearts)
of the cards that a player receives.

If we look for nouns in this description, there are several candidates for ob-
jects: game, player, hand, card, deck, value, and suit. Of these, the value and
the suit of a card are simple values, and they will just be represented as instance
variables in a Card object. In a complete program, the other five nouns might
be represented by classes. But let’s work on the ones that are most obviously
reusable: card, hand, and deck.
If we look for verbs in the description of a card game, we see that we can
shuffle a deck and deal a card from a deck. This gives use us two candidates
for instance methods in a Deck class: shuffle() and dealCard(). Cards can
be added to and removed from hands. This gives two candidates for instance
methods in a Hand class: addCard() and removeCard(). Cards are relatively pas-
sive things, but we need to be able to determine their suits and values. We will
discover more instance methods as we go along.

The Deck Class:

First, we’ll design the deck class in detail. When a deck of cards is first created, it
contains 52 cards in some standard order. The Deck class will need a constructor
to create a new deck. The constructor needs no parameters because any new deck
is the same as any other. There will be an instance method called shuffle() that

62
.. A Case Study: Card Games

will rearrange the 52 cards into a random order. The dealCard() instance method
will get the next card from the deck. This will be a method with a return type of
Card, since the caller needs to know what card is being dealt. It has no parameters
– when you deal the next card from the deck, you don’t provide any information
to the deck; you just get the next card, whatever it is. What will happen if there
are no more cards in the deck when its dealCard() method is called? It should
probably be considered an error to try to deal a card from an empty deck, so
the deck can throw an exception in that case. But this raises another question:
How will the rest of the program know whether the deck is empty? Of course,
the program could keep track of how many cards it has used. But the deck itself
should know how many cards it has left, so the program should just be able to
ask the deck object. We can make this possible by specifying another instance
method, cardsLeft(), that returns the number of cards remaining in the deck.
This leads to a full specification of all the methods in the Deck class:
/** Constructor. Create a shuffled deck of cards.
* @precondition: None
* @postcondition: A deck of 52, shuffled cards is created.*/
public Deck()

/** Shuffle all cards in the deck into a random order.


* @precondition: None
* @postcondition: The existing deck of cards with the cards
* in random order.*/
public void shuffle()

/** Returns the size of the deck


* @return the number of cards still left in the deck.
* @precondition: None
* @postcondition: The deck is unchanged. */
public int size()

/** Determine if this deck is empty


* @return true if this deck has no cards left.
* @precondition: None
* @postcondition: The deck is unchanged. */
public boolean isEmpty()

/** Deal one card from this deck


* @return a Card from the deck.
* @precondition: The deck is not empty
* @postcondition: The deck has one less card. */
public Card deal()

This is everything you need to know in order to use the Deck class. Of course,
it doesn’t tell us how to write the class. This has been an exercise in design, not

63
. Object Oriented Analysis and Design

in programming. With this information, you can use the class in your programs
without understanding the implementation. The description above is a contract
between the users of the class and implementors of the class—it is the “public
interface” of the class.

The Hand Class:

We can do a similar analysis for the Hand class. When a hand object is first created,
it has no cards in it. An addCard() instance method will add a card to the hand.
This method needs a parameter of type Card to specify which card is being added.
For the removeCard() method, a parameter is needed to specify which card to
remove. But should we specify the card itself (“Remove the ace of spades”), or
should we specify the card by its position in the hand (“Remove the third card
in the hand”)? Actually, we don’t have to decide, since we can allow for both
options. We’ll have two removeCard() instance methods, one with a parameter
of type Card specifying the card to be removed and one with a parameter of type
int specifying the position of the card in the hand. (Remember that you can
have two methods in a class with the same name, provided they have different
types of parameters.) Since a hand can contain a variable number of cards, it’s
convenient to be able to ask a hand object how many cards it contains. So, we
need an instance method getCardCount() that returns the number of cards in
the hand. When I play cards, I like to arrange the cards in my hand so that cards
of the same value are next to each other. Since this is a generally useful thing to
be able to do, we can provide instance methods for sorting the cards in the hand.
Here is a full specification for a reusable Hand class:
/** Create a Hand object that is initially empty.
* @precondition: None
* @postcondition: An empty hand object is created.*/
public Hand() {

/** Discard all cards from the hand , making the hand empty.
* @precondition: None
* @postcondition: The hand object is empty. */
public void clear() {

/** If the specified card is in the hand , it is removed.


* @param c the Card to be removed.
* @precondition: c is a Card object and is non -null.
* @postcondition: The specified card is removed if it
exists.*/
public void removeCard(Card c) {

/** Add the card c to the hand.


* @param The Card to be added.

64
.. A Case Study: Card Games

* @precondition: c is a Card object and is non -null.


* @postcondition: The hand object contains the Card c
* and now has one more card.
* @throws NullPointerException is thrown if c is not a
* Card or is null. */
public void addCard(Card c) {

/** Remove the card in the specified position from the hand.
* @param position the position of the card that is to be
* removed , where positions start from zero.
* @precondition: position is valid i.e.
* 0 < position < number cards
* @postcondition: The card in the specified position is
* removed and there is one less card in the hand.
* @throws IllegalArgumentException if the position does
* not exist in the hand. */
public void removeCard(int position) {

/** Return the number of cards in the hand.


* @return int the number of cards in the hand
* @precondition: none
* @postcondition: No change in state of Hand. */
public int getCardCount() {

/** Gets the card in a specified position in the hand.


* (Note that this card is not removed from the hand !)
* @param position the position of the card that
* is to be returned
* @return Card the Card at the specified position.
* @throws IllegalArgumentException if position does not
exist.
* @precondition: position is valid i.e.
* 0 < position < number cards.
* @postcondition: The state of the Hand is unchanged. */
public Card getCard(int position) {

/** Sorts the cards in the hand in suit order and in value
order
* within suits. Note that aces have the lowest value , 1.
* @precondition: none
* @postcondition: Cards of the same
* suit are grouped together , and within a suit the cards
* are sorted by value. */
public void sortBySuit() {

/** Sorts the cards in the hand so that cards are sorted into
* order of increasing value. Cards with the same value

65
. Object Oriented Analysis and Design

* are sorted by suit. Note that aces are considered


* to have the lowest value.
* @precondition: none
* @postcondition: Cards are sorted in order of increasing
value.*/
public void sortByValue() {

The Card Class

The class will have a constructor that specifies the value and suit of the card that is
being created. There are four suits, which can be represented by the integers 0, 1,
2, and 3. It would be tough to remember which number represents which suit, so
I’ve defined named constants in the Card class to represent the four possibilities.
For example, Card.SPADES is a constant that represents the suit, spades. (These
constants are declared to be public final static ints. It might be better to use
an enumerated type, but for now we will stick to integer-valued constants. I’ll
return to the question of using enumerated types in this example at the end of
the chapter.) The possible values of a card are the numbers 1, 2, ..., 13, with 1
standing for an ace, 11 for a jack, 12 for a queen, and 13 for a king. Again, I’ve
defined some named constants to represent the values of aces and face cards.
A Card object can be constructed knowing the value and the suit of the card.
For example, we can call the constructor with statements such as:
card1 = new Card( Card.ACE, Card.SPADES ); // Construct ace of
spades.
card2 = new Card( 10, Card.DIAMONDS ); // Construct 10 of
diamonds.
card3 = new Card( v, s ); // This is OK , as long as v and s
// are integer expressions.

A Card object needs instance variables to represent its value and suit. I’ve
made these private so that they cannot be changed from outside the class, and
I’ve provided getter methods getSuit() and getValue() so that it will be possi-
ble to discover the suit and value from outside the class. The instance variables
are initialized in the constructor, and are never changed after that. In fact, I’ve
declared the instance variables suit and value to be final, since they are never
changed after they are initialized. (An instance variable can be declared final
provided it is either given an initial value in its declaration or is initialized in
every constructor in the class.)
Finally, I’ve added a few convenience methods to the class to make it eas-
ier to print out cards in a human-readable form. For example, I want to be
able to print out the suit of a card as the word “Diamonds”, rather than as the
meaningless code number 2, which is used in the class to represent diamonds.
Since this is something that I’ll probably have to do in many programs, it makes

66
.. A Case Study: Card Games

sense to include support for it in the class. So, I’ve provided instance methods
getSuitAsString() and getValueAsString() to return string representations of
the suit and value of a card. Finally, I’ve defined the instance method toString()
to return a string with both the value and suit, such as “Queen of Hearts”. Re-
call that this method will be used whenever a Card needs to be converted into a
String, such as when the card is concatenated onto a string with the + operator.
Thus, the statement
System.out.println( "Your card is the " + card );

is equivalent to
System.out.println( "Your card is the " + card.toString() );

If the card is the queen of hearts, either of these will print out
``Your card is the Queen of Hearts''.
Here is the complete Card class. It is general enough to be highly reusable, so
the work that went into designing, writing, and testing it pays off handsomely
in the long run.
/** An object of type Card represents a playing card from a
* standard Poker deck , including Jokers. The card has a suit ,
which
* can be spades , hearts , diamonds , clubs , or joker. A spade ,
heart ,
* diamond , or club has one of the 13 values: ace , 2, 3, 4, 5,
6, 7,
* 8, 9, 10, jack , queen , or king. Note that "ace" is
considered to be
* the smallest value. A joker can also have an associated
value;
* this value can be anything and can be used to keep track of
several
* different jokers.
*/
public class Card {
public final static int SPADES = 0; // Codes for the 4
suits.
public final static int HEARTS = 1;
public final static int DIAMONDS = 2;
public final static int CLUBS = 3;

public final static int ACE = 1; // Codes for the


non -numeric cards.
public final static int JACK = 11; // Cards 2 through 10
have their
public final static int QUEEN = 12; // numerical values for
their codes.

67
. Object Oriented Analysis and Design

public final static int KING = 13;

/** This card 's suit , one of the constants SPADES , HEARTS ,
DIAMONDS ,
* CLUBS. The suit cannot be changed after the card is
* constructed. */
private final int suit;

/** The card 's value. For a normal cards , this is one of the
values
* 1 through 13, with 1 representing ACE. The value cannot be
changed
* after the card is constructed. */
private final int value;

/** Creates a card with a specified suit and value.


* @param theValue the value of the new card. For a regular
card (non -joker),
* the value must be in the range 1 through 13, with 1
representing an Ace.
* You can use the constants Card.ACE , Card.JACK , Card.QUEEN ,
and Card.KING.
* For a Joker , the value can be anything.
* @param theSuit the suit of the new card. This must be one
of the values
* Card.SPADES , Card.HEARTS , Card.DIAMONDS , Card.CLUBS , or
Card.JOKER.
* @throws IllegalArgumentException if the parameter values
are not in the
* permissible ranges */
public Card(int theValue, int theSuit) {
if (theSuit != SPADES && theSuit != HEARTS && theSuit !=
DIAMONDS &&
theSuit != CLUBS )
throw new IllegalArgumentException("Illegal playing
card suit");
if (theValue < 1 || theValue > 13)
throw new IllegalArgumentException("Illegal playing
card value");
value = theValue;
suit = theSuit;
}

/** Returns the suit of this card.


* @returns the suit , which is one of the constants
Card.SPADES ,

68
.. A Case Study: Card Games

* Card.HEARTS , Card.DIAMONDS , Card.CLUBS */


public int getSuit() {
return suit;
}

/** Returns the value of this card.


* @return the value , which is one the numbers 1 through 13.
*/
public int getValue() {
return value;
}

/** Returns a String representation of the card 's suit.


* @return one of the strings "Spades", "Hearts", "Diamonds",
"Clubs ".*/
public String getSuitAsString() {
switch ( suit ) {
case SPADES: return "Spades";
case HEARTS: return "Hearts";
case DIAMONDS: return "Diamonds";
case CLUBS: return "Clubs";
default: return "Null";
}
}

/** Returns a String representation of the card 's value.


* @return for a regular card , one of the strings "Ace", "2",
* "3", ..., "10", "Jack", "Queen", or "King ". */
public String getValueAsString() {
switch ( value ) {
case 1: return "Ace";
case 2: return "2";
case 3: return "3";
case 4: return "4";
case 5: return "5";
case 6: return "6";
case 7: return "7";
case 8: return "8";
case 9: return "9";
case 10: return "10";
case 11: return "Jack";
case 12: return "Queen";
default: return "King";
}
}

/** Returns a string representation of this card , including

69
. Object Oriented Analysis and Design

both
* its suit and its value. Sample return values
* are: "Queen of Hearts", "10 of Diamonds", "Ace of Spades",
*/
public String toString() {
return getValueAsString() + " of " + getSuitAsString();
}

} // end class Card

2.5 Example: A Simple Card Game

We will finish this section by presenting a complete program that uses the Card
and Deck classes. The program lets the user play a very simple card game called
HighLow. A deck of cards is shuffled, and one card is dealt from the deck and
shown to the user. The user predicts whether the next card from the deck will
be higher or lower than the current card. If the user predicts correctly, then the
next card from the deck becomes the current card, and the user makes another
prediction. This continues until the user makes an incorrect prediction. The
number of correct predictions is the user’s score.
My program has a method that plays one game of HighLow. This method has
a return value that represents the user’s score in the game. The main()method lets
the user play several games of HighLow. At the end, it reports the user’s average
score.
Note that the method that plays one game of HighLow returns the user’s score
in the game as its return value. This gets the score back to the main program,
where it is needed. Here is the program:

import java.util.Scanner;
/**
* This program lets the user play HighLow , a simple card game
* that is described in the output statements at the beginning of
* the main () method. After the user plays several games ,
* the user 's average score is reported.
*/
public class HighLow {

Scanner keyboard = new Scanner(System.in);

public static void main(String[] args) {

System.out.println("This program lets you play the simple


card game,");

70
.. Example: A Simple Card Game

System.out.println("HighLow. A card is dealt from a deck


of cards.");
System.out.println("You have to predict whether the next
card will be");
System.out.println("higher or lower. Your score in the
game is the");
System.out.println("number of correct predictions you make
before");
System.out.println("you guess wrong.");
System.out.println();

int gamesPlayed = 0; // Number of games user has


played.
int sumOfScores = 0; // The sum of all the scores from
// all the games played.
double averageScore; // Average score , computed by
dividing
// sumOfScores by
gamesPlayed.
boolean playAgain; // Record user 's response when
user is
// asked whether he wants to
play
// another game.

do {
int scoreThisGame; // Score for one game.
scoreThisGame = play(); // Play the game and get the
score.
sumOfScores += scoreThisGame;
gamesPlayed++;
System.out.print("Play again? ");
playAgain = keyboard.nextBoolean();
} while (playAgain);

averageScore = ((double)sumOfScores) / gamesPlayed;

System.out.println();
System.out.println("You played " + gamesPlayed + "
games.");
System.out.printf("Your average score was %1.3f.\n",
averageScore);

} // end main ()

/**

71
. Object Oriented Analysis and Design

* Let's the user play one game of HighLow , and returns the
* user 's score on that game. The score is the number of
* correct guesses that the user makes.
*/
private static int play() {

Deck deck = new Deck(); // Get a new deck of cards , and


// store a reference to it in
// the variable , deck.

Card currentCard; // The current card , which the user


sees.

Card nextCard; // The next card in the deck. The user


tries
// to predict whether this is higher
or lower
// than the current card.

int correctGuesses ; // The number of correct predictions


the
// user has made. At the end of
the game ,
// this will be the user 's score.

char guess; // The user 's guess. 'H' if the user


predicts that
// the next card will be higher , 'L' if
the user
// predicts that it will be lower.

deck.shuffle(); // Shuffle the deck into a random order


before
// starting the game.

correctGuesses = 0;
currentCard = deck.dealCard();
System.out.println("The first card is the " + currentCard);

while (true) { // Loop ends when user 's prediction is


wrong.

/* Get the user 's prediction , 'H' or 'L' (or 'h' or


'l '). */

System.out.print("Will the next card be higher(H) or


lower(L)? ");

72
.. Example: A Simple Card Game

do {
guess = keyboard.next().charAt(0);
guess = Character.toUpperCase(guess);
if (guess != 'H' && guess != 'L')
System.out.print("Please respond with H or L:
");
} while (guess != 'H' && guess != 'L');

/* Get the next card and show it to the user. */

nextCard = deck.dealCard();
System.out.println("The next card is " + nextCard);

/* Check the user 's prediction. */

if (nextCard.getValue() == currentCard.getValue()) {
System.out.println("The value is the same as the
previous card.");
System.out.println("You lose on ties. Sorry!");
break; // End the game.
}
else if (nextCard.getValue() > currentCard.getValue()) {
if (guess == 'H') {
System.out.println("Your prediction was
correct.");
correctGuesses++;
}
else {
System.out.println("Your prediction was
incorrect.");
break; // End the game.
}
}
else { // nextCard is lower
if (guess == 'L') {
System.out.println("Your prediction was
correct.");
correctGuesses++;
}
else {
System.out.println("Your prediction was

73
. Object Oriented Analysis and Design

incorrect.");
break; // End the game.
}
}

/* To set up for the next iteration of the loop , the


nextCard
becomes the currentCard , since the currentCard has
to be
the card that the user sees , and the nextCard will be
set to the next card in the deck after the user makes
his prediction. */

currentCard = nextCard;
System.out.println();
System.out.println("The card is " + currentCard);

} // end of while loop

System.out.println();
System.out.println("The game is over.");
System.out.println("You made " + correctGuesses
+ " correct
predictions.");
System.out.println();

return correctGuesses;

} // end play ()
} // end class

74
Chapter

3
Inheritance

Object oriented languages have a feature called inheritance. Inheritance enables


you to define a new class based upon an existing class. The new class is similar
to the existing class, but has additional member variables and methods. This
makes programming easier because you can build upon an existing class instead
of starting out from scratch. Inheritance (and other features of object oriented
languages) is responsible for the enormous success of modern software. Program-
mers are able to build upon previous work and to continuously improve and up-
grade existing software. For example, graphical user interface programming is
done by using inheritance with the basic graphical classes that are contained in
a standard library. The classes needed for a particular application are created by
customizing these basic classes.

3.1 Introduction to Inheritance

Problems with Hacking Code Instead of creating new classes from old classes by
inheritance, you could just copy the source code for the old class and modify it
so that it does exactly what you want. But this leads to problems.
If you have the source code for a class, you could copy the code and change
it to do what you wanted. Before object oriented programming that was what
was done. But there are at least two problems with this:

1. It is hard to stay organized. Say that you already have several dozen classes
and that you need additional classes based on the original ones. Also, say
that you need several classes based on the new classes. You will end up with

75
. Inheritance

(a) Inheritance between classes (b) Inheritance: Parent and child


Figure .: Inheritance

dozens of source files which are all versions of other source files that have
been changed in various ways. Without careful planning you will end up
with an unorganized, inconsistent, buggy mess.

2. You need to study the original code. Say that you have a complicated class
that basically does what you want, but you need a small modification. If
you edit the source code, even to make a small change, you risk break-
ing something. So you must study the original code to be sure that your
changes are correct. This may not be easy.

The automatic inheritance mechanism of Java greatly relieves both of these


problems. In a diagram (see Fig. 4.1) showing inheritance, an arrow points from
the new class to the class it is based upon. The arrow is sometimes labeled “is a”.
In this chapter, clouds represent classes and rectangles represent objects. This
style of representing classes comes from the book Object-oriented Analysis and
Design, by Grady Booch (Addison-Wesley, 1994). In official UML (Unified
Modeling Language) both classes and objects are represented with rectangles.

Single Inheritance The class that is used to define a new class is called a parent
class (or superclass or base class.) The class based on the parent class is called a
child class (or subclass or derived class.)
In Java, (unlike with humans) children inherit characteristics from just one
parent. This is called single inheritance. Some languages allow a child to inherit
from more than one parent. This is called multiple inheritance. With multiple
inheritance, it is sometimes hard to tell which parent contributed what character-
istics to the child (as with humans). Java avoids these problems by using single
inheritance.

76
.. Introduction to Inheritance

Figure .: Inheritance Diagram

Figure .: Is-a Relationship

A parent can have any number of children. But a child can have only one
parent. There are three sets of phrases for describing inheritance relationships:
parent/child, base class/derived class, superclass/subclass.
Programmers use all three sets interchangebly.

Is-a Relationship A parent class cannot inherit characteristics from its child
class. Inheritance goes in only one direction.
The picture shows a parent class and a child class. The line between them
shows the “is-a” relationship. The arrow points to the parent class from the child
class. The picture can be read as “a Ford is-a automobile.” The phrase “is-a” is
in common use in computer science. The arrow between a child and parent is
sometimes called an “is-a link”. The clouds represent classes. This picture does
not show objects.

Inheritance is between classes, not between objects.

77
. Inheritance

Figure .: Objects

A parent class is a blueprint that is followed when an object is constructed. A


child class of the parent is another blueprint (that looks much like the original),
but with added features. The child class is used to construct objects that look like
the parent’s objects, but with added features.
The picture shows a parent class and a child class, and some objects that have
been constructed from each. These objects are shown as rectangles (to convey
the idea that they are more real than the classes, which are only designs). In the
picture, “Joe’s car,” “Mary’s Ford,” and “Bob’s Ford” represent actual objects. The
cloudy classes represent designs, not actual objects. Objects are constructed as a
program runs. An object is constructed by following a description in a class file,
the result of compiling a Java source program.

Hierarchies This picture shows a hierarchy of classes. It shows that “Ford is-a
automobile,” “Nissan is-a automobile,” and that “VW is-a automobile.” It also
shows that “Sentra is-a Nissan.” In a hierarchy, each class has at most one parent
but might have several children classes. The class at the top of the hierarchy has
no parent. This class is called the root of the hierarchy.
A class may be the parent for a child class and may be a child of another class.
Just as with human relationships, a person is a child of some humans and a parent
to others. The syntax for deriving a child class from a parent class is:
class childClass extends parentClass {
// new characteristics of the child class go here

78
.. Introduction to Inheritance

Figure .: Hierarchies

Video Store Example Programming in Java consists mostly of creating class hi-
erarchies and instantiating objects from them. The Java Development Kit gives
you a rich collection of base classes that you can extend to do your work.
Here is a program that uses a class Video to represent videos available at a
rental store. Inheritance is not explicitly used in this program (so far).
class Video {
String title; // name of the item
int length; // number of minutes
boolean avail; // is the video in the store?

// constructor
public Video( String ttl ) {
title = ttl; length = 90; avail = true;
}

// constructor
public Video( String ttl, int lngth ) {
title = ttl; length = lngth; avail = true;
}

public void show() {


System.out.println( title + ", " + length + " min.
available:" + avail );
}

79
. Inheritance

public class VideoStore {


public static void main ( String args[] ) {
Video item1 = new Video("Jaws", 120 );
Video item2 = new Video("Star Wars" );

item1.show();
item2.show();
}
}

The Video class has basic information in it, and could be used for documen-
taries and instructional videos. But more information is needed for movie videos.
Let us make a class that is similar to Video, but now includes the name of the di-
rector and a rating.
class Movie extends Video {
String director; // name of the director
String rating; // G, PG , R, or X

// constructor
public Movie( String title, int length, String dir,
String rate ) {
super( title, length ); //use the super class 's constructor
director = dir;
rating = rate; // initialize what 's new to Movie
}
}

The class Movie is a subclass of Video. An object of type Movie has the follow-
ing members in it:
title inherited from Video
length inherited from Video
avail inherited from Video
show() inherited from Video
director defined in Movie
rating defined in Movie

Both classes are defined: the Video class can be used to construct objects of
that type, and now the Movie class can be used to construct objects of the Movie
type.
A child class inherits both variables and methods e.g. the method show in
the superclass Video is inherited in the subclass Movie.

3.2 Constructors in Sub-classes

The class definition for Video has a constructor that initializes the fields (or at-
tributes) of Video objects. The class Movie has a constructor that initializes the

80
.. Constructors in Sub-classes

data of Movie objects.


The constructor for class Movie looks like this:
// constructor
public Movie( String title, int length, String dir,
String rate ){
super(title, length); // use the super class 's constuctor
director = dir; // initialize the members new to Movie
rating = rate; // initialize the members new to Movie
}

The statement super(title, length) invokes a constructor of the parent to


initialize some of the data. There are two constructors in the parent. The one that
is invoked is the one that matches the argument list in super(title, length).
Then the next two statements initialize the members that only Movie has. Note:
super() must be the first statement in the subclass’s constructor.
The statement that invokes the parent’s constructor called is super() because
the parent of a class is sometimes called its superclass.
A constructor for a child class always starts with an invocation of one of the
constuctors in the parent class. If the parent class has several constructors then
the one which is invoked is determined by matching argument lists. For example,
we could define a second constructor for Movie that does not include an argument
for length. It starts out by invoking the parent constructor that does not have an
argument for length:
// alternate constructor
public Movie( String title, String dir, String rate){
super( title ); // invoke the matching parent class
constructor
director = dir;
rating = rate; // initialize members unique to Movie
}

A child constructor always invokes a parent constructor. Examine the follow-


ing proposed constructor for Movie:
// proposed constructor
public Movie( String ttl, int lngth, String dir, String rtng ){
title = ttl; // do what the parent 's constuctor does.
length = lngth;
avail = true;

director = dir;
rating = rtng;
}

81
. Inheritance

It looks like there is no need to invoke the parent class’s constructor since all
variables are initialized in this one. However a constructor from the parent class
is always invoked even if you don’t explicity ask for it.
The Java compiler automatically (automagically) inserts a call to super in the
first line of the constructor. The above code can be regarded as “shorthand” for
this:
// proposed constructor
public Movie( String ttl, int lngth, String dir, String rtng ){
super(); // invoke the parent 's no -argument
constructor
title = ttl; // do what the parent 's constuctor does.
length = lngth;
avail = true;

director = dir; rating = rtng;


}

As always, super() comes first, even if you don’t write it in. If the parent
does not have a no-argument constructor, then using this “shorthand” causes a
syntax error. In our program the class definition for Video (the superclass) lacks
a no-argument constructor. The proposed constructor (above) calls for such a
constructor so it would cause a syntax error.
A class has a no-argument constructor when:

1. The programmer can write a no-argument constructor for a class.


2. If the programmer does not write any constructors for a class, then a no-
argument constructor (called the default constructor) is automatically sup-
plied.
3. If the programmer writes even one constructor for a class then the default
constructor is not automatically supplied.

In the example program, the class definition for Video includes a constructor,
so the default constructor was not automatically supplied. So the constructor
proposed for Movie causes a syntax error.

The Object Class

Remember the rule: every constructor starts out with a super() constructor. If
you don’t explicitly put it in, then the Java compiler automatically puts it in for
you. Now look at the definition of Video.
This is correct. All classes have a parent class (a super class) except one. The
class at the top of the Java class hierarchy is called Object. If a class definition does

82
.. Overriding Methods

not specify a parent class then it automatically has Object as a parent class. The
compiler automatically assumes, for example: class Video extends Object {
. . . }. It is not possible to write a class definition that does not have Object
as its ultimate ancestor.
If you define a class that does not explicitly extend another class, then it
automatically extends Object. If you define the class so that it extends a class
other than Object, then that class either extends another class or extends Object.
Now that class in turn must either extend another class or extend Object. There
is no way to end the chain except by (ultimately) extending Object. You might
cleverly try to have class A extend class B, and have class B extend class A. But
this (and other loops) is not allowed. Ultimately, every Java object inherits its
object-like behavior from the class Object. When an object is constructed a chain
of constructors is invoked starting with the one in Object and ending with the
one in the requested class. The fundamental parts of the object are put together
by the constructor in Object, then additional parts are added down the chain
until the requested class is reached. Construction starts with the constructor in
Object because each constructor (except Object’s constructor) starts by invoking
its super() constructor (with or without arguments).
The parent class does not have to be recompiled each time a new child class
is derived from it.

3.3 Overriding Methods

Here is an example program that makes use of the two classes:


public class VideoStore{
public static void main ( String args[] ) {
Video item1 = new Video("Microcosmos", 90 );
Movie item2 = new Movie("Jaws", 120, "Spielberg", "PG" );
item1.show();
item2.show();
}
}

The program prints:


Microcosmos, 90 min. available:true
Jaws, 120 min. available:true

The statement item2.show() calls the show() method of item2. This method
was inherited without change from the class Video. This is what it looks like:
public void show() {
System.out.println( title + ", " + length +
" min. available:" + avail ); }

83
. Inheritance

It does not mention the new variables that have been added to objects of type
Movie, so nothing new is printed out. We cannot change show() in Video to in-
clude the line System.out.println( "dir: "+ director + rating ); because
the class Video does not define the instance variables director and rating, so its
show() method can’t use them.
We need a new show() method in the class Movie:
// added to class Movie
public void show() {
System.out.println( title + ", " + length +
" min. available:" + avail );
System.out.println("dir: " + director + " " + rating );
}

Now, even though the parent has a show() method the new definition of
show() in the child class will override the parent’s version.
A child’s method overrides a parent’s method when it has the same signature
as a parent method. Now the parent has its method, and the child has its own
method with the same signature. (Remember that the signature of a method is
the name of the method and its parameter list.)
An object of the parent type includes the method given in the parent’s def-
inition. An object of the child type includes the method given in the child’s
definition.
With the change in the class Movie the following program will print out the
full information for both items.
class videoStore{
public static void main ( String args[] ) {
Video item1 = new Video("Microcosmos", 90 );
Movie item2 = new Movie("Jaws", 120, "Spielberg", "PG" );
item1.show();
item2.show();
}
}

The line item1.show() calls the show() method defined in Video, and the line
item2.show() calls the show() method defined in Movie.
Microcosmos, 90 min. available:true Jaws,
120 min. available:true
dir: Spielberg PG

Notice that the Movie class includes some code that is already written.

Using super in a Childʼs Method Sometimes (as in the example) you want a child
class to have its own method, but that method includes everything the parent’s
method does. You can use the super reference in this situation. For example,
here is Video’s method:

84
.. Overriding Methods

public void show() {


System.out.println( title + ", " + length + "
min. available:" + avail ); }

Here is Movie’s method without using super:


public void show() {
System.out.println( title + ", " + length
+ " min. available:" + avail );
System.out.println( "dir: " + director+ " " + rating );
}

Movie’s method would better be written using super:


public void show() {
super.show();
System.out.println( "dir: " + director + " " + rating ); }

Unlike the case when super is used in a constructor, inside a method super
does not have to be used in the first statement. Two reasons why using super in
this way is a good thing to do.

1. You should not have to write the same code more than once.
2. A change made to the method in the parent class is inherited by the child
class.

Overloaded vs Overridden Methods

Methods can be overloaded or overridden, but constructors can be only over-


loaded. Overloaded methods and constructors let you use the same method name
(or constructor) but with different argument lists. Overriding lets you redefine a
method in a subclass, when you need new subclassspecific behavior. Overloaded
methods let you reuse the same method name in a class, but with different ar-
guments (and optionally, a different return type). Overloading a method often
means you’re being a little nicer to those who call your methods, because your
code takes on the burden of coping with different argument types rather than
forcing the caller to do conversions prior to invoking your method. The rules for
overloaded methods are simple:

1. Overloaded methods must change the argument list.


2. Overloaded methods can change the return type.
3. Overloaded methods can change the access modifier.
4. A method can be overloaded in the same class or in a subclass.

85
. Inheritance

Overloading Example
public void changeSize(int size, String name, float pattern) {
}

The following methods are legal overloads of the changeSize() method:


public void changeSize(int size, String name) { }
public int changeSize(int size, float pattern) { }

Deciding which of the matching methods to invoke is based on the argu-


ments. Overriding lets you redefine a method in a subclass, when you need new
subclass specific behavior. Rules for overridden methods
1. The argument list must exactly match that of the overridden method.
2. The return type must exactly match that of the overridden method.
3. The access level must not be more restrictive than that of the overridden
method.
4. The access level can be less restrictive than that of the overridden method.

public class Animal {


public void eat() { }
}

The following are all illegal overrides.


private void eat() { } // Access modifier
// is more restrictive

public void eat(String food) { } //A legal overload ,


//not an override , because
//the argument list changed

public String eat() { } //Not an override because of


//the return type , but not an
// overload either because ’ theres
//no change in the argument list

3.4 Another Example subclass

To reiterate the ideas discussed thus far, we give an example of another subclass
of Video.

86
.. Another Example subclass

Figure .: Many Subclasses

Music Video Class So far the video rental application has two classes: Video
and Movie. Say that you wanted to create a new class, MusicVideo that will be
like Video but will have two new instance variables: artist (the name of the per-
former) and category (“R&B”, “Pop”, “Classical”, “Other” ). Both of these will
be Strings.
The MusicVideo class will need its own constructor and its own show() method.
The new class looks like this:
class MusicVideo extends Video{
String artist;
String category;

// The constructor will go here

// The show () method will go here

The MusicVideo class is a subclass of Video. The Movie class is not shown, but
is also a subclass of Video. Remember that a class can have several sub-classes.
MusicVideo inherits title, length, and avail from its parent and adds artist and
category. Notice that MusicVideo inherits only from its parent Video. It does not
inherit anything from its sibling class Movie. Here is the definition so far:
We need a constructor for MusicVideo. We will use four parameters for title,
length, artist and category. Initialize avail to true.
// constructor
public MusicVideo ( String ttl, int len, String art, String
cat ) {
super( ttl, len );
artist = art;
category = cat;
}

87
. Inheritance

Note that the super reference must be in the first statement of the constructor.
To finish the MusicVideo class, we write a show() method for it using a super
reference to do the things already done by the parent class.
The show() method for MusicVideo can use super.show() where ever it needs
to. It is the first statement (below) because that is where it makes the most sense.
public void show() {
System.out.println( title + ", " + length +
" min. available:" + avail );
}
}

You may have noticed a major design flaw in the definitions of these classes
— none has a rental price! Say that it was your job to fix this problem. How can
this be fixed?
All videos have a rental price, so the fixes should be made to the parent class
Video. A new variable rent should be added to the parent class. Then modify
its constructor and its show() method. The two child classes will inherit these
changes. Fixing the parent class fixes all of its children.

3.5 Inheritance and Types

Inheritance defines is-a relationships between objects. For example, a MusicVideo


is-a Video. This means that an object of type MusicVideo is automatically an ob-
ject of type Video. Thus, subclass objects can be assigned to superclass variables
(but NOT vice-versa).

A variable that can hold a reference to an object of class A can also hold
a reference to an object belonging to any subclass of A.

This is the substitution principle: you can substitute a subclass reference for
a superclass reference. Thus, given the definitions of the classes above, any of the
following are legal:
Movie mymovie = new Movie("The Return of the King", 180,
"Jackson", "PG");
Video myvideo = new Movie("Jaws", 120, "Spielberg", "PG" );
myvideo = mymovie ;

The variable myvideo now refers to a Movie object. This is logical since a Movie
is-a Video.
This also works for parameters to methods: i.e. a method requiring an object
of class A as parameter, could be passed any object constructed from a subclass of
A. For example, if a method requires an object of type Video then you can pass it
any object whose type is a subclass of Video.

88
.. Inheritance and Types

public static void printDetails(Video v){


...
}

public static void main(String[] args){


Movie mymovie = new Movie("The Return of the King", 180,
"Jackson", "PG")
printDetails(mymovie)
}

You may also create arrays of type Video and populate it with objects created
from any subclass of Video.
Video [] videoArray = new Video[5];
...
v[0] = new Movie("Jaws", 120, "Spielberg", "PG" );
v[1] = new MusicVideo("LOTR Theme", 10, "Hans Zimmer","movie
score");
...
for(int i = 0; i < videoArray.length; i++){
System.out.println(v[i].getTitle());
}

Note carefully:

A subclass reference can be assigned to a superclass variable; BUT a su-


perclass reference cannot be assigned to a subclass variable.

i.e. while we can say Video v = new Movie(...); we cannot say Movie m =
new Video(...). This holds even if the superclass variable is referencing a sub-
class object: for e.g. this is illegal also:
Video myvideo = new Movie("Jaws", 120, "Spielberg", "PG" );
Movie mymovie = new Movie("The Return of the King", 180,
"Jackson", "PG");
myvideo = mymovie; // is legal
mymovie = myvideo; // is illegal

The last statement is illegal because it is an attempt to assign a superclass reference


to a subclass variable. It is illegal even though myvideo is actually referencing
a subclass object. The compiler checks the type of myvideo and because it is
declared to be of type Video, it cannot be assigned to a subclass variable.
Again this rule is logical because Video variable could potentially refer to other
types of videos that are not Movies. You are not allowed to assign a value of type
Video to a variable of type Movie because not every Video is a Movie.

89
. Inheritance

Type Checking

Assigning subclass references to superclass variables has an important consequence


that is due to the fact that Java is strongly typed. In strongly typed languages, the
type of every variable must be declared before its use. This allows the compiler
to check that each variable is being correctly used.
Once you declare a variable to be a certain type, the compiler will ensure
that it is only ever assigned values of that type (or values that are sub-types
of that type).
Recall that for primitive types the compiler checks assignments, for e.g. that a
double is not being assigned to an int. For reference types, the compiler checks
assignments and for correctness of methods calls i.e. that the method exists in
the class and the type, number and order of the parameters are correct. For e.g.
given these statements:
Video item1 = new Video("Microcosmos", 90 );
Movie item2 = new Movie("Jaws", 120, "Spielberg", "PG" );
item1.getTitle();

the compiler checks that the getTitle() method is defined in the Video class.
In all cases, when checking for correct use of variables, the compiler checks
the declared type of the variable.
The declared type of a variable is used in type checking.
So, given this:
Video item3 = new Movie("Jaws", 120, "Spielberg", "PG" );
item3.getDirector();

the compiler checks the declared type of item3 (which is Video) and a compile
error results if the getDirector() method does not exist in the Video class. Even
though item3 is actually referencing a Movie object, the compiler checks against
the declared type of the variable.

Typecasting

Java is strongly typed, but it is possible to subvert the type checking by convert-
ing variables from one type to another. Converting a variable from one type to
another is known as typecasting. Typecasting may be implicit (i.e. done with-
out the programmer doing anything) or explicit (where the programmer has to
specify a cast. )
Widening conversions are allowed automatically (implicitly), without inter-
vention from the programmer: e.g. int are typecast to double automatically.
Narrowing conversions are allowed only by explicit type casts. e.g. converting
double to int. The code below shows implicit and explicit typecasting.

90
.. Inheritance and Types

double d = 0.0;
int i = 6;

d = i; // allowed (implicit typecast)


i = d; // error (narrowing conversion)
i = (int)d; // allowed (explicit typecast)

The same ideas applies to classes and objects. You can store sub-class objects
in super-class variables since this is considered a narrowing conversion. So the
typecast is implicit. For all other conversion, an explicit typecast is required.
If you know that myVideo refers to a Movie, you can use the type cast (Movie)myVideo
to tell the compiler to treat myVideo as if it were actually of type Movie. So, you
could say myMovie = (Movie)myVideo; and you could even refer to ((Movie)myVideo).director.
Note that for object types, type-casts are checked. If myVideo refers to an
object of type MusicVideo, then the type cast (Movie)myVideo will produce an
error.
The code below shows the use of typecasting for object types.
Video myVideo = new Video();
Movie myMovie = new Movie();
// ...............................
myVideo = new Movie(); //legal -implicit typecast
myVideo = myMovie; // legal
myMovie.director = "Akira Kurosawa"; // legal
(Movie)myVideo.director = "Gavin Hood" ; // legal
myMovie = (Movie)myVideo; // legal

Note that, as explained in a previous section, the use of all variables are
checked by the compiler. So the explicit typecast in line 7 is required because
the artist field is not defined in the Video class. If the variable is not typecast,
then an error results.

the instanceof operator Java has a special operator, the instanceof operator
that determines whether or not an object is an instance of a particular class. For
example, given these definitions:
Video v = new Movie(...);

then v instanceof Movie returns true since v references a Movie object. Note
that this checks the class of the actual object being referenced, not the type of
the variable. Even though the type of v is Video, the object being referenced is a
Movie.
The method below shows how typecasting may be used to deal with different
subclass objects.
public void printDetails(Video myVideo) {
System.out.println("Title : "

91
. Inheritance

+ myVideo.title);
if (myVideo instanceof Movie) {
System.out.println("Type of Video: Movie");
System.out.println("director: "
+ (Movie)myVideo.director);
}
else if (myVideo instanceof MusicVideo) {
System.out.println("Type of Video: Music Video");
System.out.println("Artist: "
+ (MusicVideo)myVideo.artist);
}

3.6 Controlling Access to Members of a Class

Access level modifiers determine whether other classes can use a particular field
or invoke a particular method. There are two levels of access control:
• A class may be declared with the modifier public, in which case that class
is visible to all classes everywhere. If a class has no modifier (the default,
also known as package-private), it is visible only within its own package.
• A member(i.e. a field or a method) may be declared public, private, pro-
tected, or have no explicit modifier. If the member has not been declared
with a modifier, its access is known as package-private.
The private modifier specifies that the member can only be accessed in its
own class. The protected modifier specifies that the member can only be accessed
within its own package (as with package-private) and, in addition, by a subclass
of its class even if the subclass is in another package.
The following table shows the access to members permitted by each modifier.

Access Level Modifier Class Package Subclass World


public Y Y Y Y
protected Y Y Y N
no modifier Y Y N N
private Y N N N
Table .: Access Modifiers

The first column indicates whether the class itself has access. As you can see, a
class always has access to its own members. The second column indicates whether
classes in the same package as the class have access to the member. The third

92
.. Controlling Access to Members of a Class

column indicates whether subclasses of the class - declared outside this package -
have access to the member. The fourth column indicates whether all classes have
access to the member.
Access levels affect you in two ways.
1. when you use classes that come from another source, such as the classes in
the Java platform, access levels determine which members of those classes
your own classes can use.
2. when you write a class, you need to decide what access level every member
variable and every method in your class should have.

Example

Let’s look at a collection of classes and see how access levels affect visibility. The
following figure shows the four classes in this example and how they are related.

Figure .: Classes and Packages of the Example Used to Illustrate Access

Access from within the class The first class Alpha shows that all members of a
class have access to all other members within the class, regardless of the access
modifier. The isEqualTo method demonstrates that instances of the same class
have access to one another’s private members.
package one;

/**
* Access from within the Class
*/
public class Alpha {
// member variables / fields
private int privateVariable = 1;
int packageVariable = 2; // default access
protected int protectedVariable = 3;
public int publicVariable = 4;

93
. Inheritance

// methods
private void privateMethod() {
System.out.printf("privateMethod called%n");
}
void packageMethod() { // default access
System.out.printf("packageMethod called%n");
}
protected void protectedMethod() {
System.out.printf("protectedMethod called%n");
}
public void publicMethod() {
System.out.printf("publicMethod called%n");
}

/**
* A member 's access modifier determines which classes have
access to that member ,
* not which instances have access. So instances of the same
class
* have access to one another 's private members:
*/
public void isEqualTo(Alpha anotherAlpha) {
if (this.privateVariable ==
anotherAlpha.privateVariable) // legal
System.out.printf("equal");
else
System.out.printf("not equal");
}

public static void main(String[] args) {


Alpha a = new Alpha();
Alpha anotherAlpha = new Alpha();

a.privateMethod(); // legal
a.packageMethod(); // legal
a.protectedMethod(); // legal
a.publicMethod(); // legal

System.out.printf("privateVariable: %2d%n",
a.privateVariable); // legal
System.out.printf("packageVariable: %2d%n",
a.packageVariable); // legal
System.out.printf("protectedVariable: %2d%n",
a.protectedVariable); // legal

94
.. Controlling Access to Members of a Class

System.out.printf("publicVariable: %2d%n",
a.publicVariable); // legal

a.isEqualTo(anotherAlpha);
}
}

A member’s access level determines which classes have access to that member,
not which instances have access. So instances of the same class have access to one
another’s private members:

package one;
public class Alpha {
...
public boolean isEqualTo(Alpha anotherAlpha) {
if ( this.privateVariable
== anotherAlpha.privateVariable) {
// legal
return true;
} else {
return false;
}
}
}

Access from within the same package The class Beta is in the same package as
Alpha, so it has access to all Alpha’s members except those that are private.
/**
* Same package as Alpha
*/
package one;

/**
* Access from within the same package
*/
public class Beta {
public static void main(String[] args) {
Alpha a = new Alpha();
//a.privateMethod (); // illegal
a.packageMethod(); // legal
a.protectedMethod(); // legal
a.publicMethod(); // legal

// System.out.printf (" privateVariable :%2d%n",


a.privateVariable ); // illegal

95
. Inheritance

System.out.printf("packageVariable:%2d%n",
a.packageVariable); // legal
System.out.printf("protectedVariable:%2d%n",a.protectedVariable);
// legal
System.out.printf("publicVariable:%2d%n",
a.publicVariable); // legal
}
}

Access from within a subclass The class AlphaSub is in a different package but
extends Alpha. It has access to all public and protected members.
package two;

import one.Alpha;
/**
* Access from anywhere else.
* @author anban
*
*/

public class AlphaSub extends Alpha{

public void test(){


// privateVariable =9; // illegal
// privateMethod ();
// packageMethod ();
// packageVariable = 9 ; // illegal
publicVariable = 0;
publicMethod();

protectedMethod();
protectedVariable =7;
}

public static void main(String[] args) {

AlphaSub a2 = new AlphaSub();


a2.protectedMethod(); // legal
System.out.printf("protectedVariable: %2d%n",
a2.protectedVariable); // legal
}
}

96
.. Controlling Access to Members of a Class

Access from anywhere else A class anywhere else has access only to public mem-
bers.
package two;
import one.*;

public class DeltaTwo {


public static void main(String[] args) {
Alpha a = new Alpha();
//a.privateMethod (); // illegal
//a.packageMethod (); // illegal
//a.protectedMethod (); // illegal
a.publicMethod(); // legal

// System.out.printf (" privateVariable : %2d%n",


// a.privateVariable ); // illegal
// System.out.printf (" packageVariable : %2d%n",
// a.packageVariable ); // illegal
// System.out.printf (" protectedVariable : %2d%n",
// a.protectedVariable ); // illegal
System.out.printf("publicVariable: %2d%n",
a.publicVariable); // legal
}
}

97
Chapter

4
Abstract Classes,
Interfaces and Inner
Classes

4.1 Abstract Classes

We will motivate the need for Abstract classes by looking at the Shape hierarchy
of classes discussed in the previous chapter. Recall that we used Shape class as a
superclass, and particular shapes, such as rectangles, were defined as subclasses of
Shape.
Part of the Shape class is shown below:
class Shape {
Color color; // Color of the shape.

void setColor(Color newColor) {


// Method to change the color of the shape.
color = newColor; // change value of instance variable
redraw(); // redraw shape , which will appear in new
color
}

void redraw() {
// method for drawing the shape
? ? ? // what commands should go here?
}

. . . // more instance variables and methods

} // end of class Shape

The subclasses inherited from Shape:

99
. Abstract Classes, Interfaces and Inner Classes

class Rectangle extends Shape {


void redraw() {
... // commands for drawing a rectangle
}
... // possibly , more methods and variables
}

class Oval extends Shape {


void redraw() {
... // commands for drawing an oval
}
... // possibly , more methods and variables
}

class RoundRect extends Shape {


void redraw() {
... // commands for drawing a rounded rectangle
}
... // possibly , more methods and variables
}

The problem we had to deal with is: what does the redraw() method in
the Shape class do? How should it be defined? The fact is that the class Shape
represents an abstract idea of a shape, and there is no way to draw such a thing.
One might then think that we should leave out the redraw() method from
the Shape class. This would not work for several reasons:
1. It has to be there, or it would be illegal to call it in the setColor() method
of the Shape class. We wrote the setColor method to automatically call
redraw() method everytime we changed the color.

2. Even if the setColor method did call the redraw() method, the bigger
problem is that it would be illegal to write “oneShape.redraw() ;”, where
oneShape is a variable of type Shape. Code such as the following would not
compile:
Shape oneShape = new Rectangle();
oneShape.redraw();

The compiler would complain that oneShape is a variable of type Shape and
there is no redraw() method in the Shape class. We would then be forced
to typecast oneShape.
Another option is have an empty method, i.e. a method with no statements:
void redraw() {

100
.. Abstract Classes

This is an untidy solution–we seem to be forced to do something unnatural. It


also does not force us to override the method. We can, legally, create subclasses
that do not have the redraw() method.
You may wonder why we have a Shape class in the first place, as we do not
intend to ever call the redraw() method of the Shape class. There is also no reason
to ever construct an actual object of type Shape - we intend only to work with the
concrete subclasses. But, the Shape definition gives us the following important
benefit: We can declare variables of type Shape that will refer to objects of one of
the subclasses. This will allow us to write code that manipulates Shape objects in
general without having to worry about particular concrete shapes. For example,
we could write a method that takes a Shape object as an argument and that will
manipulate any object of any subclass of Shape:
public void shrinkShape(Shape s, int amount){
// code here
}

We could also write generic loops that manipulate collections of shapes:


Shape[] arrayOfShapes = new Shape[4];
...
for (int i = 0; i < arrayOfShapes.length; i++){
arrayOfShapes[i].redraw();
}

A neat solution to the problems discussed above is the notion of an abstract


class. An abstract class is one that is not used to construct objects, but only used
for making subclasses. An abstract class exists to express the common properties
of all its subclasses.

An abstract class in Java is a class that is never instantiated. Its purpose is


to be a parent to several related classes. The children classes inherit from
the abstract parent class.

An abstract class is declared abstract in the class header:


abstract class Shape {
...
}

A class that is not abstract is called a concrete class - i.e. all classes we have
seen thus far are concrete classes. Even though it can not be instantiated, an
abstract class can define methods and variables that child classes inherit. However,
abstract classes will also have abstract methods.
An abstract method has no implementation and is declared to be abstract by
using the abstract modifier.

101
. Abstract Classes, Interfaces and Inner Classes

abstract public void redraw();


// abstract method -- must be defined in
// concrete subclasses

An abstract method definition consists of:


• optional access modifier (public, private, and others),
• the reserved word abstract,
• the class of the return value,
• a method signature,
• a semi-colon.
No curly braces or method body follow the signature.
An abstract method has no body. (It has no statements.) It declares an
access modifier, return type, and method signature followed by a semi-
colon. A non-abstract child class inherits the abstract method and must
define a non-abstract method that matches the abstract method.
Note the difference between an abstract method and an empty method:
public void redraw() {}
//A method with no statements

An abstract method has no implementation, so it has no braces.


A programmer would normally make a method , such as the redraw(), an
abstract method, since it is never meant to be called. It is there only to tell the
computer that all Shapes understand the redraw message. Thus, code such as the
following will compile:
Shape s = new Rectangle();
s.redraw()

The redraw() that runs will be the one defined in the rectangle class. As an
abstract method, redraw exists only to specify the common interface of all the
actual, concrete versions of redraw() in the subclasses of Shape.
The definition of the Shape class is given below:
abstract class Shape {

private Color color; // color of shape.

// method to change the color of the shape


public void setColor(Color newColor) {
color = newColor; // change value of instance variable

102
.. Abstract Classes

redraw(); // redraw shape , which will appear in new


color
}

// abstract method -- must be defined in


// concrete subclasses
public abstract void redraw();
... // more instance variables and methods

} // end of class Shape

Note that it contains fields and concrete methods in addition to abstract meth-
ods.
Abstract classes are extended like any other class. For example the subclass
Rectangle could be defined as follows:

class Rectangle extends Shape {


private double length;
private double width;

public void redraw() {


... // commands for drawing a rectangle
}
... // possibly , more methods
}

An abstract child of an abstract parent does not have to define concrete (non-
abstract) methods for the abstract methods it inherits. This means that there may
be several steps between an abstract base class to a child class that is completely
non-abstract. When extending an abstract class you can:
• leave some or all of the abstract methods undefined. In this case, you must
then declare this subclass as abstract as well.
• define all methods (ie. provide implementations). In this case the subclass
is no longer abstract.
If a class has one or more abstract methods it must be declared to be ab-
stract. An abstract class may have methods that are not abstract (the usual sort
of method). These methods are inherited by children classes in the usual way.
• A non-abstract child class of an abstract parent class must override each of
the abstract methods of its parent. A non-abstract child must override each
abstract method inherited from its parent by defining a method with the
same signature and same return type.
– Objects of the child class will include this method.

103
. Abstract Classes, Interfaces and Inner Classes

• A child may define additional methods with signatures different from the
parent’s method.

– Child objects will include these methods in addition to the first one.

• It is an error if a child defines a method with the same signature as a parent


method, but with a different return type.

These rules are not really as terrible as they seem. After working with inheri-
tance for a while the rules will seem clear. Here is an abstract class Parent.
abstract class Parent {
public abstract int compute( int x, String j);
}

Here is a child of Parent:


class Child extends Parent {
public int compute( int x, String j )
{ . . . }
}

The child’s compute() method correctly overrides the parent’s abstract method.
For e.g. the following does not correctly override the parent’s abstract method:
class Child extends Parent
{
public double compute( int x, String j )
{ . . . }
}

It is an error if a child defines a method with the same signature as a parent


method, but with a different return type.
Non-abstract children must override the abstract methods of their parents.
Here is our situation:
class Parent {
public abstract int compute( int x, String j);
}

class Child extends Parent{


// child 's compute method
}

Th child class is not declared to be abstract so it must override the compute


method. The below shows legal methods that may be added to the Child class
and some illegal methods.

104
.. Abstract Classes

Method defined in Child Override, Overload or Error?


public int compute(int x,String j){...} Override
public int compute(int stuff, String str ){...} Override (Parameter names
don’t matter, just their types.)
public int Compute(int x, String j){...} Additional Method
public int compute(String j, int x){...} Overload–the parameter order
matters.
public double compute(int x, String j){...} Error–if the signature is the
same, the return type must
match the parent.

Using Abstract classes Abstract classes cannot be instantiated but can be used
as variable types. The following is a legal use of Shape:
Shape s = new Rectangle();
s.redraw();

But, Shape s = new Shape(); is illegal because abstract classes may not be in-
stantiated.
If child class defines just one method, and that method has the same name as
the parent’s abstract method, but with a different signature then the child class
must be abstract. Since the child did not define a method with the same signature
and same return type as the parent, it must be declared to be abstract. It is OK
to have an abstract child of an abstract parent.

Abstract Class Summary

1. It is illegal to try to create actual objects of abstract types, and the computer
will report an error if you try to do so.

2. Every subclass of an abstract class must implement of the abstract methods


must itself be declared.

3. A subclass of an abstract class can declare its own fields and methods. These
methods may or may not be abstract. If they are abstract, then the class
must be declared as abstract.

4. An Abstract Class is declared as abstract and may contain some methods


that have no implementation ie. you only provide the method signature.

5. An abstract method acts as a placeholder:they are meant to be implemented


in the subclasses.

6. They can contain both abstract and concrete methods.

105
. Abstract Classes, Interfaces and Inner Classes

7. Abstract classes cannot be instantiated—they are meant only to be used as


super/base classes.
8. When extending an abstract class you can:
• leave some or all of the abstract methods undefined. In this case, you
must then declare this subclass as abstract as well.
• define all methods (ie. provide implementations). In this case the
subclass is no longer abstract.

4.2 Interfaces

Java allows single inheritance only. This means that a child class inherits from
only one parent class. Usually this is all you need. But sometimes multiple
inheritance would be convenient. Interfaces give Java some of the advantages of
multiple inheritance without the disadvantages.
With object oriented programming, you define software objects that mimic
“real world” objects. This makes programs easier to think about and more reliable.
In the real world, you often think about an object in several different ways. You
can think of your car as a vehicle or as taxable property. It would be convenient
if software objects, also, could be thought of in several ways. But a Java object
belongs to just one class. An interface describes aspects of a class other than those
that it inherits from its parent.
An interface is a set of requirements that the class must implement. An
interface is a list of constants and method declarations. The method decla-
rations DO NOT include an implementation (there is no method body).
A class that implements an interface must implement each of the methods
listed in the interface.
A class can extend one parent class to inherit the methods and instance vari-
ables of that parent. A class can also implement an interface to gain additional
methods and constants. However, the additional methods must be explicitly
written as part of the class definition. The interface is a list of requirements that
the class definition must explicitly meet (through code, not through inheritance).
For example, a class Car might extend the Vehicle class. This gives it all the meth-
ods and instance variables of Vehicle, by inheritance. If Car also implements the
Taxable interface, then its definition must contain code for all the methods listed
in Taxable.

An interface lists only constants and methods.

An interface definition looks like this:

106
.. Interfaces

interface InterfaceName {
constant definitions

method declarations (without implementations).


}

A method declaration is simply an access modifier, a return type, and a method


signature followed by a semicolon. All methods in an interface definition are
implicitly abstract i.e. they are assumed abstract so the abstract modifier is not
required..
This looks somewhat like a class definition. But no objects can be constructed
from it. However, objects can be constructed from a class that implements an
interface. A class implements an interface by doing this:
class SomeClass extends SomeParent implements interfaceName {

A class always extends just one parent but may implement several interfaces.

(Review: ) If a class definition omits extends SomeParent, then it extends


the Object class.

Here is an example interface definition. The constants don’t have to be sepa-


rated from the methods, but doing so makes the interface easier to read.
interface MyInterface{
public static final int aConstant = 32; // a constant
public static final double pi = 3.14159; // a constant

public void methodA( int x ); // a method


declaration
public double methodB(); // a method
declaration
}

A method in an interface cannot be made private. A method in an interface


is public by default. The constants in an interface are public static final by
default. Therefore, it is not necessary to use these modifiers. Making use of the
defaults, the above interface is equivalent to the following:
interface MyInterface{
int aConstant = 32; // a constant
double pi = 3.14159; // a constant

void methodA( int x ); // a method declaration


double methodB(); // a method declaration
}

107
. Abstract Classes, Interfaces and Inner Classes

The second interface (above) is the preferred way to define an interface. The
defaults are assumed and not explicitly coded.

• A class that implements an interface must implement each method in the


interface.

• Methods from the interface must be declared public in the class.

• Constants from the interface can be used as if they had been defined in the
class.

• Constants should not be redefined in the class.

The following definition is incorrect


interface SomeInterface{
public final int x = 32;
public double y;

public double addup( );


}

because variables (such as y) cannot be put in an interface. Only constants and


method declarations.

Implementing an Interface

A class definition must always extend one parent, but it can implement zero or
more interfaces:
class SomeClass extends Parent implements SomeInterface {

class definition body

The body of the class definition is the same as always. However, since it
implements an interface the body must have a definition of each of the methods
declared in the interface. The class definition can use access modifiers as usual.
Here is a class definition that implements three interfaces:
public class BigClass extends Parent implements InterfaceA,
InterfaceB, InterfaceC {

class definition body

108
.. Interfaces

Now BigClass must provide a method definition for every method declared in
the three interfaces. Here is another class definition:
public class SmallClass implements InterfaceA {

class definition body

Any number of classes can implement the same interfaces.


The above class definition is correct. You might wonder that it does not
extend a base class, but it does. If no other class is extended, Object is the base
class.
SmallClass extends Object and implements InterfaceA

Example Problem

Let us create a database program for a store. The store sells:

1. Goods, each of which has the attributes:

• description
• price

The types of goods are:

• Food—with an attribute “calories”. Food objects are not taxable.


• Toy—with an attribute “minimumAge”. Toy objects are taxable.
• Book—with an attribute “author”. Book objects are taxable.

There are many things that are taxable that are not goods, such as services or
entertainment. Also, not all goods are taxable. So we want to have the concept
“taxable” as a separate concept, not part of the concept of Goods. Here is what
the concept Taxable looks like:

• A Taxable item,

– has a taxRate of 6 percent,


– has a calculateTax() method.

When implemented in Java, these concepts will appear as classes and an interface.
The concepts and the implementation of each concept is given in the table
below:

109
. Abstract Classes, Interfaces and Inner Classes

Figure .: Interfaces

Concept Parent Class, Child Class, or Interface?


Goods Parent Class
Food Child Class
Toy Child Class
Book Child Class
Taxable Interface
The picture (see Fig. ??) that shows the classes and the interface.
The children classes extend their parent. This is shown with a solid arrow
pointing at the parent. The solid arrows show inheritance. The three children
inherit the display() method from their parent, Goods. Two of the classes im-
plement the interface. This is shown with a dotted arrow pointing at the interface.
The dotted arrows show what a class must implement. The Toy and Book classes
must implement the calculateTax() method.
According to the picture, Food will have a display() method, Toy will have
a display() method and calculateTax() method.
Here is a class definition for Goods:
public class Goods{
private String description;
private double price;

public Goods( String des, double pr ) {


description = des;
price = pr;
}

public void display() {

110
.. Interfaces

System.out.println( "item: " + description + " price: " +


price );
}
}

The child class Food extends the parent class. It uses super to use the parent’s
constructor and the parent’s display method.
public class Food extends Goods{
private double calories;

public Food( String des, double pr, double cal) {


super( des, pr );
calories = cal ;
}

public void display() {


super.display( );
System.out.println( "calories: " + calories );
}
}

It would be incorrect to reverse the order of the statments in the constructor


for Food. The super must come first.
Here is Taxable: A Taxable item,
• has a taxRate of 6 percent, which should be a double constant.
• and a calculateTax() method. which should return a double value.
The Taxable interface looks like this:
interface Taxable{
final double taxRate = 0.06 ;
double calculateTax() ;
}

final means that what follows is a constant, not a variable. Variables are not
allowed in interfaces. In fact, the final can be omitted since the compiler will au-
tomatically make the identifier a constant. The = value part cannot be omitted.
The method declaration (in the second line) is public by default.

Adding another Class Since taxRate is a constant, it must be set to a value, here,
6 percent. Here is a definition of Toy. Recall that it:
• Has a parent, Goods.
• Adds a variable, minimumAge.

111
. Abstract Classes, Interfaces and Inner Classes

• Is taxable.

public class Toy extends Goods implements Taxable{


private int minimumAge;

public Toy( String des, double pr, int min) {


super( des, pr );
minimumAge = min ;
}

public void display() {


super.display() ;
System.out.println( "minimum age: " + minimumAge );
}

public double calculateTax() {


return price * taxRate ;
}
}

The constant taxRate is used in the calculateTax() method just as if it had been
defined in the Toy class. Also, it can be used in methods of the class other than
those listed in the interface.
The calculateTax() method must be made public.
Any number of classes can implement the same interface.
There is one remaining class in our example, Book, which looks like this:

• Has a parent Goods.


• Adds a variable author.
• Is taxable.

You might wish to review the diagram that shows the relationships between
the classes.
public class Book extends Goods implements Taxable{
private String author;

public Book( String des, double pr, String auth) {


super( des, pr );
author = auth ;
}

public void display() {


super.display() ;
System.out.println( "author: " + author );

112
.. Interfaces

public double calculateTax() {


return price * taxRate ;
}
}

Note that the taxRate for Book is the same as for Toy. By using an interface, a
constant can be used by several classes. This helps keep the classes consistent.
Here is a tiny program that tests the classes.
public class Store{

public static void main ( String[] args ) {


Goods gd = new Goods( "bubble bath", 1.40 );
Food fd = new Food ( "ox tails", 4.45, 1500 );
Book bk = new Book ( "Emma", 24.95, "Austin" );
Toy ty = new Toy ( "Legos", 54.45, 8 );

gd.display();

fd.display();

ty.display();
System.out.println("Tax is: " + ty.calculateTax() + "\n" );

bk.display();
System.out.println("Tax is: " + bk.calculateTax() + "\n" );

}
}

The calculateTax() method is only used with objects whose class imple-
ments the interface. Here is a picture that shows the classes and their objects:

In the picture, clouds represent classes. Arrows with pointed head connect
child classes to parent classes. The dotted rectangle represents the interface; a
dotted arrow shows which classes implement it. Rectangles represent objects.
Arrows with square head connect an object to its class.
These 4 objects could be kept in an array. The array would be of type Goods.

Array of Goods Objects Here is a modified testing program that uses an array:
public class Store
{

public static void main ( String[] args )

113
. Abstract Classes, Interfaces and Inner Classes

Figure .: Objects and Interfaces

Figure .: The Array

{
Goods[] inventory = new Goods[10];
inventory[0] = new Goods( "bubble bath", 1.40 );
inventory[1] = new Food ( "ox tails", 4.45, 1500 );
inventory[2] = new Book ( "Emma", 24.95, "Austin" );
inventory[3] = new Toy ( "Leggos", 54.45, 8 );

inventory[0].display();
inventory[1].display();
inventory[2].display();
inventory[3].display();
}
}

Since each child class is-a Goods, an array of type Goods[] can be used with any
of them. The array inventory has 10 slots, but the program uses only 4 of them,
like this: As always, each slot of the array is a reference variable which can refer

114
.. Interfaces

to an object that has been created with a new. Each of the classes Toy and Book
are taxable.

Interface as a Type An interface can be used as a data type for a reference


variable. Since Toy and Book implement Taxable, they can both be used with a
reference variable of type Taxable:
public static void main ( String[] args ) {
Taxable item1 = new Book ( "Emma", 24.95, "Austin" );
Taxable item2 = new Toy ( "Leggos", 54.45, 8 );

System.out.println( "Tax on item 1 "+ item1.calculateTax() );


System.out.println( "Tax on item 2 "+ item2.calculateTax() );

The compiler has been told in the interface that all Taxable objects will have
a calculateTax() method, so that method can be used with the variables.
The following will not work? item1.display(); All the compiler has been
told is that item1 implements the methods in the interface Taxable. The display()
method is not in the interface.

Type Casts

When you use a variable of type Taxable you are asking to use the “taxable” aspect
of the object. Many different kinds of objects might be referred to by the vari-
able. (In a larger program there may be Taxable classes that are not Goods.) The
compiler can only use the methods it knows that the object must have—-those
in the interface.
However, you can use a type cast to tell the compiler that in a particular
statement in the program the variable will refer to an object of a specific class:
public static void main ( String[] args ) {
Taxable item1 = new Book ( "Emma", 24.95, "Austin" );
System.out.println( "Tax on item 1 "+ item1.calculateTax() );

((Book)item1).display();
}

This program is not very sensibly written, since if the variable item1 were
of type Book everything would work without the need for a type cast. But in
programs with more complicated logic such casts are sometimes needed. The
outer parentheses is needed in: ((Book)item1).display(); because you want
to:
• First, cast item1 to type Book.

115
. Abstract Classes, Interfaces and Inner Classes

• Then, invoke the display() method that objects of type Book have.

When is a Type Cast Needed? Now examine the following:


public static void main ( String[] args ) {
Book book ;
Taxable tax = new Book ( "Emma", 24.95, "Austin" );

book = (Book)tax;
book.display();
System.out.println( "Tax on item 1 "+ book.calculateTax() );
}

The type cast is necessary to tell the compiler that the variable tax in this case
contains a Book object.
Now consider the following code:
public static void main ( String[] args ) {
Goods toy ;
Taxable tax = new Toy ( "Building Blocks", 1.49, 6 );

toy = tax;
toy.display();
System.out.println( "Tax: "+ toy.calculateTax() );
}

Type casts are necessary in the above code. Here is one way.
public static void main ( String[] args ) {
Goods toy ;
Taxable tax = new Toy ( "Building Blocks", 1.49, 6 );

toy = (Toy)tax;
toy.display();
System.out.println( "Tax: "+ ((Taxable)toy).calculateTax() );
}

The first type cast must cast tax to a type Goods (or an ancestor). The second
must cast toy to Taxable or to a class that implements Taxable.
Here is another possible way:
public static void main ( String[] args ) {
Goods toy ;
Taxable tax = new Toy ( "Building Blocks", 1.49, 6 );

toy = (Goods)tax;
toy.display();
System.out.println( "Tax: "+ ((Toy)toy).calculateTax() );
}

116
.. Interfaces

public static void main ( String[] args ) Goods toy ; Taxable tax = new Toy (
”Building Blocks”, 1.49, 6 );
toy = (Goods)tax; toy.display(); System.out.println( ”Tax: ”+ ((Toy)toy).calculateTax()
);

default methods in interfaces

Java version 8, makes a few useful additions to interfaces including the idea of de-
fault methods. Unlike the usual abstract methods in interfaces, a default method
has an implementation. When a class implements the interface, it does not have
to provide an implementation for the default method—although it can do so
if it wants to provide a different implementation. Essentially, default methods
are inherited from interfaces in much the same way that ordinary methods are
inherited from classes. This moves Java partway towards supporting multiple in-
heritance. It’s not true multiple inheritance, however, since interfaces still cannot
define instance variables.
A default method in an interface must be marked with the modifier default.
It can optionally be marked public but, as for everything else in interfaces, de-
fault methods are automatically public and the public modifier can be omitted.
Here is an example.:
public interface Readable { // represents a source of input
public char readChar(); // read the next character from the
input
default public String readLine() { // read up to the next
line feed
StringBuilder line = new StringBuilder();
char ch = readChar();
while (ch != ’\’n) {
line.append(ch);
ch = readChar();
}
return line.toString();
}
}

A concrete class that implements this interface must provide an implementation


for readChar(). It will inherit a definition for readLine() from the interface, but
can provide a new definition if required. Note that the default readLine() calls
the abstract method readChar(), whose definition will only be provided in the
implementing class. The reference to readChar() in the definition is polymor-
phic. The default implementation of readLine() is one that would make sense
in almost any class that implements Readable. Here’s a rather silly example of a
class that implements Readable, including a main() routine that tests the class.
Can you figure out what it does?

117
. Abstract Classes, Interfaces and Inner Classes

public class Stars implements Readable {


public char readChar() {
if (Math.random() > 0.02)
return ’’*;
else
return ’\’n;
}
public static void main(String[] args) {
Stars stars = new Stars();
for (int i = 0 ; i < 10; i++ ) {
String line = stars.readLine();
System.out.println( line );
}
}
}

Default methods provide Java with a capability similar to something called a


“mixin” in other programming languages, namely the ability to mix functionality
from another source into a class. Since a class can implement several interfaces,
it is possible to mix in functionality from several different sources.

Additional Facts about Interfaces

There are features of interfaces that the example did not show: A class can imple-
ment several interfaces:
class SomeClass extends
SomeParent implements InterfaceA, InterfaceB, InterfaceC {

Now SomeClass must implement all the methods listed in all the interfaces.
The interfaces cannot contain several definitions of the same constant. To
ensure consistancy, a constant should be defined only once, in one interface.
However, it is OK if two interfaces ask for the same method. A class that im-
plements both interfaces only needs to provide one complete method definition
to satisfy both interfaces.
An interface can be made public. In fact, this is usually what is done. When
a class or interface is public it must be the only public class or interface in the file
that contains it.
A public interface can be implemented by any class in any file. Many graph-
ical user interface components implement public interfaces. You must use them
to work with the GUI features of Java.
An interface cannot be made private: private would mean that nobody could
use it, which is not sensible.

118
.. Nested Classes

An interface can be an extension of another interface (but not an extension


of a class):
public interface ExciseTaxable extends Taxable{
double extraTax = 0.02;
double calculateExtra();
}

A complex hierarchy of interfaces can be constructed using this feature. This


is an advanced feature which you will probably not need to use.
A class cannot extend an interface. Only classes are extended, and a class may
extend only one class.
The differences between abstract classes and interfaces is summarized in the
table below:
Variables Constructors Methods
Abstract No restrictions. Constructors are in- No restrictions
class voked by subclasses
through constructor
chaining. An abstract
class cannot be instan-
tiated using the new
operator.
Interface All variables must be No constructors. An in- All methods must be
public static final terface cannot be instan- public abstract instance
tiated using the new op- methods
erator.

4.3 Nested Classes

class is a high-level building block of a program, representing a poten-


A tially complex idea and its associated data and behaviors. Sometimes we cre-
ate simpler, small classes specifically to support the work of a larger class. Such
trivial classes are often useful and even essential. For these cases, Java, allows the
nesting of one class inside another class. There are other good reasons for nesting
the definition of one class inside another class. The nested class has privileged
access to the members of the class in which it is nested.
In Java, a nested class is any class whose definition is inside the definition of
another class. It is also possible to nest a class inside a method. Nested classes
can be either named or anonymous. A named nested class can be either static or
non-static.

119
. Abstract Classes, Interfaces and Inner Classes

Static Nested Classes

The definition of a static nested class looks just like the definition of any other
class, except that it is nested inside another class and it has the modifier static
as part of its declaration. A static nested class is part of the static structure of the
containing class. It can be used inside that class to create objects in the usual way.
If it is used outside the containing class, its name must indicate its membership
in the containing class. That is, the full name of the static nested class consists of
the name of the class in which it is nested, followed by a period, followed by the
name of the nested class for e.g. MyOuterClass.MyInnerClass..... This is similar
to other static components of a class: A static nested class is part of the class itself
in the same way that static member variables are parts of the class itself.
For example, consider a class named WireFrameModel that represents a set
of lines in three-dimensional space. (Such models are used to represent three-
dimensional objects in graphics programs.) Suppose that the WireFrameModel
class contains a static nested class, Line, that represents a single line. Then, outside
of the class WireFrameModel, the Line class would be referred to as WireFrameModel.Line.
This follows the normal naming convention for static members of a class. The
definition of the WireFrameModel class with its nested Line class would look, in
outline, like this:
public class WireFrameModel {
. . . // other members of the WireFrameModel class

static public class Line {


// Represents a line from the point (x1 ,y1 ,z1)
// to the point (x2 ,y2 ,z2) in 3-dimensional space.
double x1, y1, z1;
double x2, y2, z2;
} // end class Line

. . . // other members of the WireFrameModel class

} // end WireFrameModel

The full name of the nested class is WireFrameModel.Line. That name can be used,
for example, to declare variables. Inside the WireFrameModel class, a Line object
would be created with the constructor “new Line()”. Outside the class, “new
WireFrameModel.Line()” would be used.
A static nested class has full access to the static members of the containing
class, even to the private members. Similarly, the containing class has full access
to the members of the nested class, even if they are marked private. This can
be another motivation for declaring a nested class, since it lets you give one class
access to the private members of another class without making those members

120
.. Nested Classes

generally available to other classes. Note also that a nested class can itself be
private, meaning that it can only be used inside the class in which it is nested.
When you compile the above class definition, two class files will be created.
Even though the definition of Line is nested inside WireFrameModel, the compiled
Line class is stored in a separate file. The name of the class file for Line will be
WireFrameModel$Line.class.

Inner Classes

Non-static nested classes are referred to as inner classes. Inner classes are actually
associated with an object rather than with the class in which its definition is
nested.
Any non-static member of a class is not part of the class itself (although its
source code is contained in the class definition). The non-static members of a
class specify what will be contained in objects that are created from that class.
This is true for inner classes as well. Each object that belongs to the containing
class has its own copy of the nested class. This copy has access to all the instance
methods and instance variables of the object, even to those that are declared
private. The two copies of the inner class in two different objects differ because
the instance variables and methods they refer to are in different objects. In fact,
the rule for deciding whether a nested class should be static or non-static is simple:
If the nested class needs to use any instance variable or instance method from the
containing class, make the nested class non-static. Otherwise, it might as well be
static.
In most cases, an inner class is used only within the class where it is defined.
When that is true, using the inner class is really not much different from using
any other class. You can create variables and declare objects using the simple
name of the inner class in the usual way.
From outside the containing class, however, an inner class has to be referred to
using a name of the form 〈variableName〉.〈NestedClassName〉, where 〈variableName〉
is a variable that refers to the object that contains the inner class. In order to cre-
ate an object that belongs to an inner class, you must first have an object that
belongs to the containing class.
Consider a class that represents poker games. This class might include a
nested class to represent the players of the game. The structure of the PokerGame
class could be:
public class PokerGame { // Represents a game of poker.
class Player { // Represents one of the players in this
game.
.
.
.

121
. Abstract Classes, Interfaces and Inner Classes

} // end class Player

private Deck deck; // A deck of cards for playing the game.


private int pot; // The amount of money that has been bet.
. . .
} // end class PokerGame

If game is a variable of type PokerGame, then, game contains its own copy of
the Player class. In an instance method of a PokerGame object, a new Player object
would be created by saying “new~Player()”, just as for any other class. You could
also create a Player object outside the PokerGame class with an expression such as
“game.new~Player()”.
The Player object will have access to the deck and pot instance variables in the
PokerGame object. Each PokerGame object has its own deck and pot and Players.
Players of that poker game use the deck and pot for that game; players of another
poker game use the other game’s deck and pot. That’s the effect of making the
Player class non-static. This is the most natural way for players to behave. A
Player object represents a player of one particular poker game. If Player were an
independent class or a static nested class, on the other hand, it would represent
the general idea of a poker player, independent of a particular poker game.

Anonymous Inner Classes

In some cases, you might find yourself writing an inner class and then using that
class in just a single line of your program. In cases like this you have the option of
using an anonymous inner class. An anonymous class is created with a variation
of the new operator that has the form
new superclass-or-interface(parameter-list){
methods-and-variables
}

This constructor defines a new class, without giving it a name, and it simultane-
ously creates an object that belongs to that class. This form of the new operator
can be used in any statement where a regular “new” could be used. The intention
of this expression is to create: “a new object belonging to a class that is the same
as 〈superclass-or-interface 〉 but with these 〈methods-and-variables〉 added.” The ef-
fect is to create a uniquely customized object, just at the point in the program
where you need it. Note that it is possible to base an anonymous class on an
interface, rather than a class. In this case, the anonymous class must implement
the interface by defining all the methods that are declared in the interface. If an
interface is used as a base, the 〈parameter-list 〉 must be empty. Otherwise, it can
contain parameters for a constructor in the 〈superclass〉.
Anonymous classes are often used for handling events in graphical user inter-
faces, and we will encounter them several times in the chapters on GUI program-

122
.. Nested Classes

ming. Consider the Drawable interface, which is defined earlier in this section.
Suppose that we want a Drawable object that draws a filled, red, 100-pixel square.
Rather than defining a new, separate class and then using that class to create the
object, we can use an anonymous class to create the object in one statement:
Drawable redSquare = new Drawable() { void draw(Graphics g) {
g.setColor(Color.RED);
g.fillRect(10,10,100,100);
}
};

Then redSquare refers to an object that implements Drawable and that draws a
red square when its draw() method is called. The semicolon at the end of the
statement is not part of the class definition; it’s the semicolon that is required at
the end of every declaration statement.
Anonymous classes are often used for actual parameters. For example, con-
sider the following simple method, which draws a Drawable in two different
graphics contexts:
void drawTwice( Graphics g1, Graphics g2, Drawable figure ) {
figure.draw(g1);
figure.draw(g2);
}

When calling this method, the third parameter can be created using an anony-
mous inner class. For example:
drawTwice( firstG, secondG, new Drawable() { void
draw(Graphics g) {
g.drawOval(10,10,100,100);
}
} );

When a Java class is compiled, each anonymous nested class will produce a
separate class file. If the name of the main class is MainClass, for example, then
the names of the class files for the anonymous nested classes will be MainClass\$1.class,
MainClass\$2.class, MainClass\$3.class, and so on.

Java 8 Lambda Expressions

The syntax for anonymous classes is cumbersome. In many cases, an anonymous


class implements an interface that defines just one method. Java 8 introduces a
new syntax that can be used in place of the anonymous class in that circumstance:
the lambda expression. Here is what the previous method call looks like using a
lambda expression:
drawTwice( firstG, secondG, g -> g.drawOval(10,10,100,100) )

123
. Abstract Classes, Interfaces and Inner Classes

The lambda expression is g -\> g.drawOval(10,10,100,100). Its meaning is,


“the method that has a parameter g and executes the code g.drawOval(10,10,100,100).”
The computer knows that g is of type Graphics because it is expecting a Draw-
able as the actual parameter, and the only method in the Drawable interface has
a parameter of type Graphics. Lambda expressions can only be used in places
where this kind of type inference can be made. The general syntax of a lambda
expression is
formal-parameter-list -> method-body

where the 〈method body〉 can be a single expression, a single method call, or a
block of statements enclosed between { and }. When the body is a single ex-
pression or function call, the value of the expression is automatically used as
the return value of the method that is being defined. The parameter list in the
lambda expression does not have to specify the types of the parameters, although
it can. Parentheses around the parameter list are optional if there is exactly one
parameter and no type is specified for the parameter; this is the form seen in the
example above. For a method with no parameters, the parameter list is just an
empty set of parentheses. Here are a few more examples of lambda expressions:
() -> System.out.println("Hello World")

g -> { g.setColor(Color.RED); g.drawRect(10,10,100,100); }

(a, b) -> a + b

(int n) -> {
while (n > 0) {
System.out.println(n);
n = n/2; }
} // lambda expressions ends here

As you can see, the syntax can still get pretty complicated. There is quite a lot
more to say about lambda expressions, but my intention here is only to briefly
introduce one of the most interesting new features in Java 8.

124
Chapter

5
Graphical User
Interface Programming

omputer users today expect to interact with their computers using a graph-
C ical user interface (GUI). GUI programs differ from traditional “straight-
line” command line programs that you have in that GUI programs are event-
driven. That is, user actions such as clicking on a button or pressing a key on the
keyboard generate events, and the program must respond to these events as they
occur.

5.1 The Basic GUI Application

he command-line programs that you have learned how to program would


T seem very alien to most computer users. Today, most people interact with
their computers exclusively through a GUI. A GUI program offers a much richer
type of user interface, where the user uses a mouse and keyboard to interact
with GUI components such as windows, menus, buttons, check boxes, text input
boxes, scroll bars, and so on.
A GUI program still has a main() method, but in general, that main routine
just creates one or more GUI components and displays them on the computer
screen. Once the GUI components have been created, they follow their own
programming—programming that tells them how to draw themselves on the
screen and how to respond to events such as being clicked on by the user.
Here is a very simple GUI “Hello World” program that says “Hello” to the
user by opening a window where the greeting is displayed:
import javax.swing.JOptionPane;

125
. Graphical User Interface Programming

public class HelloWorldGUI1 {

public static void main(String[] args) {


JOptionPane.showMessageDialog( null, "Hello World!" );
}

When this program is run, a window appears on the screen that contains the
message “Hello World!”. The window also contains an “OK” button–when the
user clicks this button, the window closes and the program ends. This program is
already doing some pretty fancy stuff. It creates a window, it draws the contents
of that window, and it handles the event that is generated when the user clicks the
button. The reason the program was so easy to write is that all the work is done
by showMessageDialog(), a static method in the built-in class JOptionPane.
If you want to do anything serious in a GUI program, there is a lot more to
learn. To give you an idea of the types of things that are involved, we’ll look at a
short GUI program that does the same things as the previous program—open a
window containing a message and an OK button, and respond to a click on the
button by ending the program—but does it all by hand instead of by using the
built-in JOptionPane class.
Here is the source code for the program. In this section, you will just get a
brief overview of GUI programming.

JFrame and JPanel

In a Java GUI program, each GUI component in the interface is represented


by an object in the program. A fundamental component is the window. The
JFrame class (which is included in the package javax.swing) is a built-in class
to represent an independent window that can act as the main window of an
application. A JFrame object comes with many of the behaviors of windows
already programmed in. In particular, it comes with the basic properties shared
by all windows, such as a titlebar and the ability to be opened and closed. Since
a JFrame comes with these behaviors, you don’t have to program them yourself!
What a JFrame doesn’t come with is content, the stuff that is contained in the
window. If you don’t add any other content to a JFrame, it will just display a
blank area.
The main program above declares a variable, window, of type JFrame and sets
it to refer to a new window object with the statement:
JFrame window = new JFrame("GUI Test");

The parameter (the string “GUI test”) in the constructor specifies the title that
will be displayed in the titlebar of the window. This line creates the window

126
.. The Basic GUI Application

Listing .: Simple Graphical User Interface.


import java . awt .∗;
import java . awt . event .∗;
import javax . swing .∗;

public class HelloWorldGUI2 {


private static class HelloWorldDisplay extends JPanel {
public void paintComponent ( Graphics g) {
super. paintComponent (g);
g . drawString ( "Hello World!" , 20, 30 );
}
}

private static class ButtonHandler implements ActionListener {


public void actionPerformed ( ActionEvent e) {
System . exit (0);
}
}

public static void main ( String [] args ) {


HelloWorldDisplay displayPanel = new HelloWorldDisplay ();
JButton okButton = new JButton ( "OK" );
ButtonHandler listener = new ButtonHandler ();
okButton . addActionListener ( listener );
JPanel content = new JPanel ();
content . setLayout (new BorderLayout ());
content . add ( displayPanel , BorderLayout . CENTER );
content . add ( okButton , BorderLayout . SOUTH );
JFrame window = new JFrame ( "GUI Test" );
window . add ( content );
window . setSize (250,100);
window . setLocation (100,100);
window . setVisible (true);
}
}

127
. Graphical User Interface Programming

object, but the window itself is not yet visible on the screen. Before making the
window visible, some of its properties are set with these statements:
window.add(content);
window.setSize(250,100);
window.setLocation(100,100);

The first line here sets the content of the window. (The content itself was created
earlier in the main program.) The second line says that the window will be 250
pixels wide and 100 pixels high. The third line says that the upper left corner
of the window will be 100 pixels from the left edge of the screen and 100 pixels
down from the top. Once all this has been set up, the window is actually made
visible on the screen with the command: window.setVisible(true);
Although the main() method ends her, the window is still on the screen and the
program as a whole does not end until the user clicks the OK button. Once
the window was opened, a new thread was created to manage the graphical user
interface, and that thread continues to run even after main() has finished.
The content that is displayed in a JFrame is called its content pane. A basic
JFrame already has a blank content pane; you can either add things to that pane
or you can replace the basic content pane entirely. In the sample program, the
line window.add(content) adds the JPanel object, content to the JFrame’s con-
tent pane. We could replace this line with window.setContentPane(content) to
replace the original blank content pane with a different component. (Remember
that a “component” is just a visual element of a graphical user interface.)
JPanel is a fundamental class in Swing. The basic JPanel is just a blank rect-
angle. There are two ways to make a useful JPanel: The first is to add other
components to the panel (i.e. to use it as container) and the second is to draw
something on the panel (i.e. use it as a drawing surface or canvas). Both of
these techniques are illustrated in the sample program. You will find two JPan-
els in the program: content, which is used to contain other components, and
displayPanel, which is used as a drawing surface.
Let’s look more closely at displayPanel. We use it as a drawing surface so
we need to customize the JPanel. We do this by creating a subclass of the JPanel
class. We could have made a standalone subclass but in this example we chose to
make a static nested class inside the HelloWorldGUI2 class. The HelloWorldDisplay
class defines just one instance method, paintComponent(), which overrides the
method of the same name in the JPanel class:
private static class HelloWorldDisplay extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString( "Hello World!", 20, 30 );
}
}

128
.. The Basic GUI Application

The paintComponent() method is called by the system when a component needs


to be painted on the screen. A component needs to be painted when it is first dis-
played, when it is moved by the user and when it is made visible by e.g. clicking
on it. In the JPanel class, the paintComponent method simply fills the panel with
the panel’s background color. The paintComponent() method in HelloWorld-
Display begins by calling super.paintComponent(g). This first calls the version
of paintComponent() that is defined in the superclass, JPanel. The superclass
method does not do much–it fills the panel with the background color. When
the paintComponent method is called, it is passed a Graphics object. Every GUI
component has a Graphics object associated with it. This object is used to do
custom painting on the component. Here, we use g.drawString() to paint the
string “Hello World!” onto the panel. The net result is that whenever a Hel-
loWorldDisplay is shown on the screen, it displays the string “Hello World!”.
We will often use JPanels in this way, as drawing surfaces. Usually, when
we do this, we will define a class that is a subclass of JPanel and we will write a
paintComponent method in that class to draw the desired content in the panel.
The subclass of JPanel can be defined either as a separate class in its own file or
as a nested class.

Components and Layout

Another way of using a JPanel is as a container to hold other components. Java


has many classes that define GUI components. Except for top-level components
like windows, components must be added to a container before they can appear
on the screen. In the sample program, the variable named content refers to
a JPanel that is used as a container. Two other components are added to that
container:
content.add(displayPanel, BorderLayout.CENTER);
content.add(okButton, BorderLayout.SOUTH);

The first component that is added to content (recall that content is a JPanel) is
displayPanel which, as discussed above, displays the message, “Hello World!”.
The second is okButton which represents the button that the user clicks to close
the window. The variable okButton is of type JButton, the Java class that repre-
sents push buttons.
The “BorderLayout” stuff in these statements has to do with how the two
components are arranged in the container. When components are added to a
container, there has to be some way of deciding how those components are ar-
ranged inside the container. This is called “laying out” the components in the
container, and the most common technique for laying out components is to use
a layout manager. A layout manager is an object that implements some pol-
icy for how to arrange the components in a container; different types of layout

129
. Graphical User Interface Programming

manager implement different policies. One type of layout manager is defined by


the BorderLayout class. In the program, the statement content.setLayout(new
BorderLayout()); creates a new BorderLayout object and tells the content panel
to use the new object as its layout manager. By adding okButton in the BorderLayout.SOUTH
position puts the button at the bottom of the panel, and putting displayPanel
in the BorderLayout.CENTER position makes it fill any space that is not taken up
by the button.
This example shows a general technique for setting up a GUI: Create a con-
tainer and assign a layout manager to it, create components and add them to
the container, and either add the container to the content pane of the window
or use the container as the content pane of a window. A container is itself a
component, so it is possible that some of the components that are added to the
top-level container are themselves containers, with their own layout managers
and components. This makes it possible to build up complex user interfaces in a
hierarchical fashion, with containers inside containers inside containers….

Events and Listeners

Thus far we have seen the structure of containers and components that sets up
the physical appearance of a GUI. We consider now the behavior of the GUI i.e.
what can the user do to the GUI and how will it respond? GUIs are largely event-
driven; that is, the program waits for and responds to event that are generated by
the user’s actions. It is impossible to predict in advance the sequence of events
that could occur at the interface. Thus, an event-driven model is used to program
GUIs. In this model, you write event-handling methods to respond to the events
that you are interested in. When an event occurs, the system finds and executes
the appropriate event-handling method .
There are at least three objects involved in event handling: the component
on which the event occurs (e.g. a button), the event itself, and the event-handler.
The most common technique for handling events in Java is to use event listeners.
A listener is an object that includes one or more event-handling methods. When
an event is detected or generated by the component, such as a button or menu,
the listener object (i.e. the event handler) is notified and responds by running
the appropriate event-handling method. The event itself (the third object) carries
information about the type of event, when it occurred, and so on. This division
of responsibilities makes it easier to organize large programs.

130
.. The Basic GUI Application

User clicks button, ActionEvent object is


button generates event sent to listener
instanceof ButtonHandler

OK
actionPerformed()
Visible button,
represented by Event-handling object
a JButton object of type ActionListener
responds to the event

As an example, consider the OK button in the sample program. When the


user clicks the button, an event is generated. This event is represented by an
object belonging to the class ActionEvent. The event that is generated is associated
with the button; we say that the button is the source of the event. The listener
object in this case is an object belonging to the class ButtonHandler, which is
defined as a nested class inside HelloWorldGUI2:
private static class ButtonHandler implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}

This class implements the ActionListener interface—a requirement for listener


objects that handle events from buttons. The event-handling method is named
actionPerformed, as specified by the ActionListener interface. This method con-
tains the code that is executed when the user clicks the button; in this case, the
code is simply a call to System.exit(), which will terminate the program.
There is one more ingredient that is necessary to get the event from the button
to the listener object: The listener object must register itself with the button as
an event listener. This is done with the statement:
okButton.addActionListener(listener);
This statement tells okButton that when the user clicks the button, the Action-
Event that is generated should be sent to listener. Without this statement, the
button has no way of knowing that there is something that would like to listen
for events from the button.
This example shows a general technique for programming the behavior of
a GUI: Write classes that include event-handling methods. Create objects that
belong to these classes and register them as listeners with the objects that will
actually detect or generate the events. When an event occurs, the listener is
notified, and the code that you wrote in one of its event-handling methods is
executed. At first, this might seem like a very roundabout and complicated way
to get things done, but as you gain experience with it, you will find that it is very
flexible and that it goes together very well with object oriented programming.

131
. Graphical User Interface Programming

This section has introduced some of the fundamentals of GUI programming.


We will spend the rest of the chapter exploring them in more detail.

5.2 Graphics and Painting

verything you see on a computer screen has to be drawn there, even the
E text. The Java API includes a range of classes and methods that are devoted
to drawing.
The physical structure of a GUI is built of components. The term compo-
nent refers to a visual element in a GUI, including buttons, menus, text-input
boxes, scroll bars, check boxes, and so on. In Java, GUI components are repre-
sented by objects belonging to subclasses of the class java.awt.Component. Most
components in the Swing GUI toolkit—although not top-level components like
JFrame—belong to subclasses of the class javax.swing.JComponent, which is it-
self a subclass of java.awt.Component. Every component is responsible for draw-
ing itself. If you want to use a standard component, you only have to add it to
your program. You don’t have to worry about painting it on the screen. That
will happen automatically, since it already knows how to draw itself.
Sometimes, however, you do want to draw on a component. You will have
to do this whenever you want to display something that is not included among
the standard, pre-defined component classes. When you want to do this, you
have to define your own component class and provide a method in that class
for drawing the component. We will use a subclass of JPanel when we need a
drawing surface of this kind, as we did for the HelloWorldDisplay class in the ex-
ample HelloWorldGUI2.java in the previous section. A JPanel, like any JCompo-
nent, draws its content in the method public void paintComponent(Graphics
g). To create a drawing surface, define a subclass of JPanel and override the
paintComponent() method with custom drawing code. Then, create an object
of this class and use it in your program. When the component has to be drawn
on the screen, the system will call its paintComponent() to do the drawing. That
is, the code that you put into the paintComponent() method will be executed
whenever the panel needs to be drawn on the screen; by writing this method,
you determine the picture that will be displayed in the panel. You do not usu-
ally call a paintComponent() method–the system calls the method. You write the
method to say what will happen when the system calls it.
The paintComponent() method has a parameter of type Graphics. The Graph-
ics object will be provided by the system when it calls your method. You need this
object to do the actual drawing. Every component you create will have its own
Graphics object associated with it. This object is called the graphics context of
that component. The Graphics object keeps track of foreground and background
colors used to paint the component, fonts used to draw the strings and thickness

132
.. Graphics and Painting

of lines used to draw shapes, amongst others. Instance methods are provided in
this class for drawing shapes, text, and images. Any given Graphics object can
draw to only one location. In this chapter, that location will always be a GUI
component belonging to some subclass of JPanel.
The Graphics class is an abstract class, which means that it is impossible to
create a graphics context directly, with a constructor. There are actually two
ways to get a graphics context for drawing on a component: First, when the
paintComponent() method of a component is called by the system, the parame-
ter to that method is a graphics context for drawing on the component. Second,
every component has an instance method called getGraphics() that returns a
graphics context that can be used for drawing on the component outside its
paintComponent() method. It is a good idea to dispose of the graphics when
you are done. This is done by calling the dispose(), for e.g. if g is a graphics
context created with getGraphics(),then you call g.dispose() when finished
using it. This releases any operating system resources that might be held by g.
The paintComponent() method in the JPanel class simply fills the panel with
the panel’s background color. When defining a subclass of JPanel for use as a
drawing surface, you will usually want to fill the panel with the background color
before drawing other content onto the panel. This is traditionally done with a
call to super.paintComponent(g), so most paintComponent() methods that you
write will have the form:
public void paintComponent(g) {
super.paintComponent(g);
. . . // Draw the content of the component.
}

A component should do all drawing operations in its paintComponent() method.


The run-time system calls paintComponent() to paint the component on the
screen. It is called when the component first appears on the screen or when the
size of the component changes, which can happen when the user resizes the win-
dow that contains the component or minimizes or maximizes the window or
when the window is uncovered etc.
Programmers should not call paintComponent() directly because the pro-
grammer does not know the state of the window at each time–it may be mini-
mized for example. The programmer can request that a component be repainted
by calling the component’s repaint() method. The method public void
repaint(); is defined in the Component class, and so can be used with any com-
ponent. You should call repaint() to inform the system that the component
needs to be redrawn. It is important to understand that the repaint() method
returns immediately, without doing any painting itself. The system will call the
component’s paintComponent() method later, as soon as it gets a chance to do
so, after processing other pending events if there are any. It is even possible that

133
. Graphical User Interface Programming

many calls to repaint() will all be handled by one call to paintComponent(), if


the calls to repaint() occur in a very short timespan.
The paintComponent() should be capable of redrawing the content of the
component on demand. This means that, to work properly, the paintComponent()
method must be smart enough to correctly redraw the component at any time.
To make this possible, a program should store data in its instance variables about
the state of the component. These variables should contain all the information
necessary to redraw the component completely. The paintComponent() method
should use the data in these variables to decide what to draw. When the program
wants to change the content of the component, it should not simply draw the new
content. It should change the values of the relevant variables and call repaint().
When the system calls paintComponent(), that method will use the new values
of the variables and will draw the component with the desired modifications.

Coordinates

The screen of a computer is a grid of little squares called pixels. The color of each
pixel can be set individually, and drawing on the screen just means setting the
colors of individual pixels.

x=0 x = width

y=0

y = height

A graphics context draws in a rectangle made up of pixels. A position in


the rectangle is specified by a pair of integer coordinates, (x,y). The upper left
corner has coordinates (0,0). The x coordinate increases from left to right, and
the y coordinate increases from top to bottom. The illustration shows a 20-pixel
by 12-pixel component (with very large pixels). A small line, rectangle, and oval
are shown as they would be drawn by coloring individual pixels.
For any component, you can find out the size of the rectangle that it occupies
by calling the instance methods getWidth() and getHeight(), which return the
number of pixels in the horizontal and vertical directions, respectively. In general,
it’s not a good idea to assume that you know the size of a component, since the
size is often set by a layout manager and can change if the component is resized

134
.. Graphics and Painting

by the user. This means that it’s a good idea to check the size of a component
before doing any drawing. For example:
public void paintComponent(Graphics g) {
super.paintComponent(g);
int width = getWidth(); // Find out the width of this
component.
int height = getHeight(); // Find out its height.
. . . // Draw the content of the component.
}

Your drawing commands will have to take the size into account. That is, they
will have to use (x,y) coordinates that are calculated based on the actual height
and width of the component. (However, if you are sure that you know the size,
using constants for the width and height can make the drawing easier.)

Colors

There are many ways to represent colors on a computer. We will use the simple
RGB color system. An RGB color is specified by three numbers that give the level
of red, green, and blue, respectively, in the color. A color in Java is an object of
the class, java.awt.Color. You can construct a new color by specifying its red,
blue, and green components. For example,
Color myColor = new Color(r,g,b);
where r, g, and b are integers in the range 0 to 255. The Color class defines sev-
eral named constants representing common colors: Color.WHITE, Color.BLACK,
Color.RED, Color.GREEN, Color.BLUE, Color.CYAN, Color.MAGENTA, Color.YELLOW,
Color.PINK, Color.ORANGE, Color.LIGHT_GRAY, Color.GRAY, and Color.DARK_GRAY.
An alternative to RGB is the HSB color system. In the HSB system, a color
is specified by three numbers called the hue, the saturation, and the brightness.
The RGB system and the HSB system are just different ways of describing the
same set of colors. It is possible to translate between one system and the other.
One of the properties (fields) of a Graphics object is the current drawing
color, which is used for all drawing of shapes and text. If g is a graphics con-
text, you can change its current drawing color with g.setColor(c), where c
is a Color. For example, if you want to draw in green, you would just say
g.setColor(Color.GREEN) before doing the drawing. The graphics context con-
tinues to use the color until you explicitly change it with another setColor()
command. If you want to know what the current drawing color is, you can
call the function g.getColor(), which returns an object of type Color. This can
be useful if you want to change to another drawing color temporarily and then
restore the previous drawing color.
Every component has an associated foreground color and background color.
Generally, the component is filled with the background color before anything

135
. Graphical User Interface Programming

else is drawn. When a new graphics context is created for a component, the
current drawing color is set to the foreground color. Note that the foreground
color and background color are properties of the component, not of a graphics
context.
The foreground and background colors of a component can be set by calling
instance methods:
component.setForeground(color) and
component.setBackground(color),
which are defined in the Component class and therefore are available for use with
any component. This can be useful even for standard components, if you want
them to use colors that are different from the defaults.

Fonts

A font represents a particular size and style of text. The same character will appear
different in different fonts. In Java, a font is characterized by a font name, a style,
and a size. The available font names are system dependent, but you can always
use the following four strings as font names: “Serif ”, “SansSerif ”, “Monospaced”,
and “Dialog”. (A “serif ” is a little decoration on a character, such as a short
horizontal line at the bottom of the letter i. “SansSerif ” means “without serifs.”
“Monospaced” means that all the characters in the font have the same width. The
“Dialog” font is the one that is typically used in dialog boxes.)
The style of a font is specified using named constants that are defined in the
Font class. You can specify the style as one of the four values:

• Font.PLAIN,
• Font.ITALIC,
• Font.BOLD, or
• Font.BOLD + Font.ITALIC.

The size of a font is an integer. Size typically ranges from about 9 to 36,
although larger sizes can also be used. The default size is 12.
Java uses the class named java.awt.Font for representing fonts. You can
construct a new font by specifying its font name, style, and size in a constructor:
Font plainFont = new Font("Serif", Font.PLAIN, 12);
Font bigBoldFont = new Font("SansSerif", Font.BOLD, 24);

Every graphics context has a current font, which is used for drawing text. You
can change the current font with the setFont() method. For example, if g is a
graphics context and bigBoldFont is a font, then the command g.setFont(bigBoldFont)
will set the current font of g to bigBoldFont. The new font will be used for any
text that is drawn after the setFont() command is given. You can find out the

136
.. Graphics and Painting

current font of g by calling the method g.getFont(), which returns an object of


type Font.
Every component also has an associated font. It can be set with the instance
method component.setFont(font), which is defined in the Component class.
When a graphics context is created for drawing on a component, the graphic
context’s current font is set equal to the font of the component.

Shapes

The Graphics class includes a large number of instance methods for drawing var-
ious shapes, such as lines, rectangles, and ovals. The shapes are specified using
the (x,y) coordinate system described above. They are drawn in the current
drawing color of the graphics context. The current drawing color is set to the
foreground color of the component when the graphics context is created, but it
can be changed at any time using the setColor() method.
Some of the drawing methods are listed here. More details may be found in
the documentation With all these commands, any drawing that is done outside
the boundaries of the component is ignored. Note that all these methods are in
the Graphics class, so they all must be called through an object of type Graphics.
It is shown here as g, but of course the name of the graphics context is up to the
programmer.
• g.drawString(String str, int x, int y) — Draws the text given by the
string str. The string is drawn using the current color and font of the
graphics context. x specifies the x-coordinate of the left end of the string. y
is the y-coordinate of the baseline of the string. The baseline is a horizontal
line on which the characters rest. Some parts of the characters, such as the
tail on a y or g, extend below the baseline.
• g.drawLine(int x1, int y1, int x2, int y2) — Draws a line from the
point (x1,y1) to the point (x2,y2). The line is drawn as if with a pen
that extends one pixel to the right and one pixel down from the (x,y)
point where the pen is located. For example, if g refers to an object of type
Graphics, then the command g.drawLine(x,y,x,y), which corresponds to
putting the pen down at a point, colors the single pixel with upper left
corner at the point (x,y). Remember that coordinates really refer to the
lines between the pixels.
• g.drawRect(int x, int y, int width, int height) — Draws the out-
line of a rectangle. The upper left corner is at (x,y), and the width and
height of the rectangle are as specified. If width equals height, then the
rectangle is a square. If the width or the height is negative, then noth-
ing is drawn. The rectangle is drawn with the same pen that is used for
drawLine(). This means that the actual width of the rectangle as drawn

137
. Graphical User Interface Programming

is width+1, and similarly for the height. There is an extra pixel along the
right edge and the bottom edge. For example, if you want to draw a rect-
angle around the edges of the component, you can say “g.drawRect(0, 0,
getWidth()-1, getHeight()-1);”. If you use “g.drawRect(0, 0, getWidth(),
getHeight());”, then the right and bottom edges of the rectangle will be
drawn outside the component and will not appear on the screen.
• g.drawOval(int x, int y, int width, int height) — Draws the out-
line of an oval. The oval is one that just fits inside the rectangle specified by
x, y, width, and height. If width equals height, the oval is a circle.
• g.drawRoundRect(int x, int y, int width, int height, int xdiam,
int ydiam) — Draws the outline of a rectangle with rounded corners. The
basic rectangle is specified by x, y, width, and height, but the corners are
rounded. The degree of rounding is given by xdiam and ydiam. The corners
are arcs of an ellipse with horizontal diameter xdiam and vertical diameter
ydiam. A typical value for xdiam and ydiam is 16, but the value used should
really depend on how big the rectangle is.
• g.draw3DRect(int x, int y, int width, int height, boolean raised)
— Draws the outline of a rectangle that is supposed to have a three-dimensional
effect, as if it is raised from the screen or pushed into the screen. The ba-
sic rectangle is specified by x, y, width, and height. The raised parameter
tells whether the rectangle seems to be raised from the screen or pushed into
it. The 3D effect is achieved by using brighter and darker versions of the
drawing color for different edges of the rectangle. The documentation rec-
ommends setting the drawing color equal to the background color before
using this method. The effect won’t work well for some colors.
• g.drawArc(int x, int y, int width, int height, int startAngle, int
arcAngle) — Draws part of the oval that just fits inside the rectangle spec-
ified by x, y, width, and height. The part drawn is an arc that extends
arcAngle degrees from a starting angle at startAngle degrees. Angles are
measured with 0 degrees at the 3 o’clock position (the positive direction of
the horizontal axis). Positive angles are measured counterclockwise from
zero, and negative angles are measured clockwise. To get an arc of a circle,
make sure that width is equal to height.
• g.fillRect(int x, int y, int width, int height) — Draws a filled-
in rectangle. This fills in the interior of the rectangle that would be drawn
by drawRect(x,y,width,height). The extra pixel along the bottom and
right edges is not included. The width and height parameters give the ex-
act width and height of the rectangle. For example, if you wanted to fill
in the entire component, you could say “g.fillRect(0, 0, getWidth(),
getHeight());”

138
.. Graphics and Painting

• g.fillOval(int x, int y, int width, int height) — Draws a filled-


in oval.
• g.fillRoundRect(int x, int y, int width, int height, int xdiam,
int ydiam) — Draws a filled-in rounded rectangle.
• g.fill3DRect(int x, int y, int width, int height, boolean raised)
— Draws a filled-in three-dimensional rectangle.
• g.fillArc(int x, int y, int width, int height, int startAngle, int
arcAngle) — Draw a filled-in arc. This looks like a wedge of pie, whose
crust is the arc that would be drawn by the drawArc method.

Graphics2D

All drawing in Java is done through an object of type Graphics. The Graphics
class provides basic commands for such things as drawing shapes and text and
for selecting a drawing color. These commands are adequate in many cases, but
they fall far short of what’s needed in a serious computer graphics program. Java
has another class, Graphics2D, that provides a larger set of drawing operations.
Graphics2D is a sub-class of Graphics, so all the methods from the Graphics class
are also available in a Graphics2D.
The paintComponent() method of a JComponent gives you a graphics context
of type Graphics that you can use for drawing on the component. In fact, the
graphics context actually belongs to the sub-class Graphics2D, and can be type-
cast to gain access to the advanced Graphics2D drawing methods:
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2;
g2 = (Graphics2D)g;
.
. // Draw on the component using g2.
.
}

One use of the Graphics2D class is turn on antialiasing. If g2 is a variable of


type Graphics2D, as in the paintComponent() method above, then the command
g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
);

turns on antialiasing in the graphics context. Aliasing is a jagged appearance that


can be seen when shapes are drawn using pixels. Antialiasing tries to reduce the
jaggedness. It can make diagonal lines and the outlines of ovals look much nicer.
It can also improve the appearance of text.

139
. Graphical User Interface Programming

Another useful command is g2.setStroke( new BasicStroke(lineWidth));


where lineWidth is an integer or a float. This command can be use to draw
thicker lines. Lines drawn after the command will be lineWidth pixels wide.
This also affects the thickness of the outlined shapes drawn by methods such as
g.drawRect and g.drawOval().

An Example

Let’s use some of the material covered in this section to write a subclass of JPanel
for use as a drawing surface. All the drawing will be done in the paintComponent()
method of the panel class. The panel will draw multiple copies of a message on a
black background. Each copy of the message is in a random color. Five different
fonts are used, with different sizes and styles. The message can be specified in the
constructor; if the default constructor is used, the message is the string “Java!”.
There is one problem with the way this class works. When the panel’s
paintComponent() method is called, it chooses random colors, fonts, and loca-
tions for the messages. The information about which colors, fonts, and locations
are used is not stored anywhere. The next time paintComponent() is called, it will
make different random choices and will draw a different picture. If you resize a
window containing the panel, the picture will be continually redrawn as the size
of the window is changed! To avoid that, you would store enough information
about the picture in instance variables to enable the paintComponent() method
to draw the same picture each time it is called.
The source code for the panel class is shown below. I use an instance variable
called message to hold the message that the panel will display. There are five in-
stance variables of type Font that represent different sizes and styles of text. These
variables are initialized in the constructor and are used in the paintComponent()
method.
The paintComponent() method for the panel simply draws 25 copies of the
message. For each copy, it chooses one of the five fonts at random, and it uses
g.setFont() to select that font for drawing. It creates a random HSB color and
uses g.setColor() to select that color for drawing. It then chooses random (x,y)
coordinates for the location of the message. The x coordinate gives the horizontal
position of the left end of the string. The formula used for the x coordinate is
“-50~+ (int)(Math.random()* (width+40))”. This gives a random integer in
the range from -50 to width-10. This makes it possible for the string to extend
beyond the left edge or the right edge of the panel. Similarly, the formula for y
allows the string to extend beyond the top and bottom.

140
.. Graphics and Painting

Here is the complete source code for the RandomStringsPanel:


Listing .: Random Strings.
import java . awt .∗;
import javax . swing . JPanel ;

/∗ ∗

∗ This panel displays 25 copies of a message . The color ,

∗ position and font of each message is selected at random .

∗/

public class RandomStringsPanel extends JPanel {

private String message ; / / Message to be displayed . Can be set in

// the constructor , if not then the string

// " Java !" is used .

private Font font1 , font2 , font3 , font4 , font5 ; // The five fonts .

/∗ ∗

∗ Default constructor creates a panel that displays the message " Java !".

∗/

public RandomStringsPanel () {
this(null); // Call the other constructor , with parameter null .

/∗ ∗

∗ Constructor : creates a panel to display 25 copies of message .

∗ @param messageString The message to be displayed . If this is null ,

∗ then the default message " Java !" is displayed .

∗/

public RandomStringsPanel ( String messageString ) {


message = messageString ;
if ( message == null)
message = "Java!" ;

font1 = new Font ( "Serif" , Font . BOLD , 14);


font2 = new Font ( "SansSerif" , Font . BOLD + Font . ITALIC , 24);
font3 = new Font ( "Monospaced" , Font . PLAIN , 30);
font4 = new Font ( "Dialog" , Font . PLAIN , 36);
font5 = new Font ( "Serif" , Font . ITALIC , 48);

setBackground ( Color . BLACK );


}

141
. Graphical User Interface Programming

Listing .: Random Strings contd.


/∗ ∗

∗ The paintComponent method is responsible for drawing the content

∗ of the panel . It draws 25 copies of the message string , using a

∗ random color , font , and position for each string .

∗/

public void paintComponent ( Graphics g) {

super. paintComponent (g); / / Call the paintComponent method from the

// superclass , JPanel . This simply fills

// entire panel with the background color , black .

Graphics2D g2 = ( Graphics2D ) g ; // ( To make the text s m o o t h e r .)

g2 . setRenderingHint ( RenderingHints . KEY_ANTIALIASING ,


RenderingHints . VALUE_ANTIALIAS_ON );

int width = getWidth ();


int height = getHeight ();

for (int i = 0; i \< 25; i++) {


// Draw one string . First , set the font to be one of the five

// available fonts , at random .

int fontNum = (int)(5∗ Math . random ()) + 1;


switch ( fontNum ) {
case 1:
g . setFont ( font1 );
break;
case 2:
g . setFont ( font2 );
break;
case 3:
g . setFont ( font3 );
break;
case 4:
g . setFont ( font4 );
break;
case 5:
g . setFont ( font5 );
break;
} // end switch

// Set the color to a bright , saturated color , with random hue .

142
.. Mouse Events

float hue = (float) Math . random ();


g . setColor ( Color . getHSBColor ( hue , 1.0F , 1.0 F ) );

// Select the position of the string , at random .

int x ,y;
x = −50 + (int)( Math . random ()∗( width +40));
y = (int)( Math . random ()∗( height +20));

// Draw the message .

g . drawString ( message ,x , y );

} // end for

} // end p a i n t C o m p o n e n t ()

} // end class RandomStringsPanel

To complete the program, here is a class with a main() method that creates
an instance of RandomStringsPanel and adds it to a frame (see Listing 5.4).
Listing .: Random Strings Frame.
import javax . swing . JFrame ;

public class RandomStrings {

public static void main ( String [] args ) {


JFrame window = new JFrame ( "Java!" );
RandomStringsPanel content = new RandomStringsPanel ();
window . setContentPane ( content );
window . setDefaultCloseOperation ( JFrame . EXIT_ON_CLOSE );
window . setLocation (120,70);
window . setSize (350,250);
window . setVisible (true);
}
}

5.3 Mouse Events

vents are central to programming for a graphical user interface. A GUI


E program doesn’t have a main() routine that outlines what will happen when
the program is run, in a step-by-step process from beginning to end. Instead, the
program must be prepared to respond to various kinds of events that can happen
at unpredictable times and in an order that the program doesn’t control. The

143
. Graphical User Interface Programming

most basic kinds of events are generated by the mouse and keyboard. The user
can press any key on the keyboard, move the mouse, or press a button on the
mouse. The user can do any of these things at any time, and the computer has
to respond appropriately.
In Java, events are represented by objects. When an event occurs, the sys-
tem collects all the information relevant to the event and constructs an object
to contain that information. Different types of events are represented by objects
belonging to different classes. For example, when the user presses one of the but-
tons on a mouse, an object belonging to a class called MouseEvent is constructed.
The object contains information such as the source of the event (that is, the com-
ponent on which the user clicked), the (x,y) coordinates of the point on the
component where the click occurred, the exact time of the click, and which but-
ton on the mouse was pressed. When the user presses a key on the keyboard,
a KeyEvent is created. After the event object is constructed, it can be passed as
a parameter to a designated method. By writing that method, the programmer
says what should happen when the event occurs.
There is a lot of processing that goes on between the time that the user presses
a key or moves the mouse and the time that a method in your program is called
to respond to the event. Fortunately, you don’t need to know much about that
processing. Every GUI program executes a loop, called an event loop of the form
while the program is still running:
Wait for the next event to occur
Call a method to handle the event

In Java, we do not explicitly program the loop–it’s part of “the system.” In this
section, we’ll look at handling mouse events in Java, and we’ll cover the frame-
work for handling events in general. The next section will cover keyboard-related
events and timer events. Java also has other types of events, which are produced
by GUI components. These will be introduced in Section 5.4.

Event Handling

For an event to have any effect, a program must detect the event and react to it.
In order to detect an event, the program must “listen” for it. Listening for events
is something that is done by an object called an event listener. An event listener
object must contain instance methods for handling the events for which it listens.
For example, if an object is to serve as a listener for events of type MouseEvent,
then it must contain the following method (among several others):
public void mousePressed(MouseEvent evt){ . . . }
The body of the method defines how the object responds when it is notified that a
mouse button has been pressed. The parameter, evt, contains information about
the event. This information can be used by the listener object to determine its
response.

144
.. Mouse Events

The methods that are required in a mouse event listener are specified in an
interface named MouseListener. To be used as a listener for mouse events, a class
must implement this MouseListener interface. (To review briefly: An interface
in Java is just a list of instance methods. A class can “implement” an inter-
face by doing two things: First, the class must be declared to implement the
interface, as in “class MouseHandler implements MouseListener” or “class
MyPanel extends JPanel implements MouseListener”; and second, the class
must include a definition for each instance method specified in the interface.
An interface can be used as the type for a variable or formal parameter. Note
that it is not enough for the class to include the specified methods–it must be
specifically declared to implement the interface.)
Many events in Java are associated with GUI components. For example,
when the user presses a button on the mouse, the associated component is the one
that the user clicked on. Before a listener object can “hear” events associated with
a given component, the listener object must be registered with the component.
If a MouseListener object, mListener, needs to hear mouse events associated with
a Component object, comp, the listener must be registered with the component
by calling
comp.addMouseListener(mListener);
The addMouseListener() method is an instance method in class Component, and
so can be used with any GUI component object. In our first few examples, we
will listen for events on a JPanel that is being used as a drawing surface.
The event classes, such as MouseEvent, and the listener interfaces, such as
MouseListener, are defined in the package java.awt.event. This means that
if you want to work with events, you should either include the line “import
java.awt.event.*;” at the beginning of your source code file or import the in-
dividual classes and interfaces.
To summarize, you must
1. Import the necessary classes and interfaces with “import java.awt.event.*;”
(or individual imports) at the beginning of your source code;
2. Declare that some class implements the appropriate listener interface, such
as MouseListener;
3. Provide definitions in that class for the methods specified by the interface;
4. Register an object of the listener class with the component that will gen-
erate the events by calling a method such as addMouseListener() in the
component.
Any object can act as an event listener, provided that it implements the ap-
propriate interface. A component can listen for the events that it itself generates.
A panel can listen for events from components that are contained in the panel.
A special class can be created just for the purpose of defining a listening object.

145
. Graphical User Interface Programming

It is a good idea to use anonymous inner classes to define listening objects, and
named nested classes can also be appropriate. You will see all of these patterns in
examples in this textbook.

MouseEvent and MouseListener

The MouseListener interface specifies these five instance methods:


public void mousePressed(MouseEvent evt);
public void mouseReleased(MouseEvent evt);
public void mouseClicked(MouseEvent evt);
public void mouseEntered(MouseEvent evt);
public void mouseExited(MouseEvent evt);

The mousePressed method is called as soon as the user presses down on one of
the mouse buttons, and mouseReleased is called when the user releases a button.
These are the two methods that are most commonly used, but any mouse listener
object must define all five methods; you can leave the body of a method empty
if you don’t want to define a response. The mouseClicked method is called if
the user presses a mouse button and then releases it, without moving the mouse.
(When the user does this, all three routines—mousePressed, mouseReleased, and
mouseClicked—will be called in that order.) In most cases, you should define
mousePressed instead of mouseClicked. The mouseEntered and mouseExited
methods are called when the mouse cursor enters or leaves the component. For
example, if you want the component to change appearance whenever the user
moves the mouse over the component, you could define these two methods.
As a first example, we will look at a small addition to the RandomStringsPanel
example from the previous section. In the new version, the panel will repaint
itself when the user clicks on it. In order for this to happen, a mouse listener
should listen for mouse events on the panel, and when the listener detects a
mousePressed event, it should respond by calling the repaint() method of the
panel.
For the new version of the program, we need an object that implements the
MouseListener interface. One way is to define a separate class, as in Listing 5.5.
This class does three of the four things that we need to do in order to han-
dle mouse events: First, it imports java.awt.event.* for easy access to event-
related classes. Second, it is declared that the class “implements MouseListener”.
And third, it provides definitions for the five methods that are specified in the
MouseListener interface. (Note that four of the methods have empty bodies, since
we don’t want to do anything in response to those events.)
We must do one more thing to set up the event handling for this example:
We must register an event-handling object as a listener with the component that
will generate the events. In this case, the mouse events that we are interested in
will be generated by an object of type RandomStringsPanel. If panel is a variable

146
.. Mouse Events

Listing .: RepaintOnClick.java


import java . awt . Component ;
import java . awt . event .∗;

/∗ ∗

∗ An object of type RepaintOnClick is a MouseListener that

∗ will respond to a mousePressed event by calling the r e p a i n t ()

∗ method of the source of the event . That is , a RepaintOnClick

∗ object can be added as a mouse listener to any Component ;

∗ when the user clicks that component , the component will be

∗ repainted .

∗/

public class RepaintOnClick implements MouseListener {

public void mousePressed ( MouseEvent evt ) {


Component source = ( Component ) evt . getSource ();
source . repaint (); // Call r e p a i n t () on the Component that was

clicked .

public void mouseClicked ( MouseEvent evt ) { }


public void mouseReleased ( MouseEvent evt ) { }
public void mouseEntered ( MouseEvent evt ) { }
public void mouseExited ( MouseEvent evt ) { }

that refers to the panel object, we can create a mouse listener object and register
it with the panel with the statements:
RepaintOnClick listener = new RepaintOnClick(); // Create
MouseListener object.
panel.addMouseListener(listener); // Register MouseListener
with the panel.

This could be done, for example, in the main() routine where the panel is created.
Once the listener has been registered in this way, it will be notified of mouse
events on the panel. When a mousePressed event occurs, the mousePressed()
method in the listener will be called. The code in this method calls the repaint()
method in the component that is the source of the event, that is, in the panel.
The result is that the RandomStringsPanel is repainted with its strings in new
random colors, fonts, and positions.

147
. Graphical User Interface Programming

Although we have written the RepaintOnClick class for use with our Random-
StringsPanel example, the event-handling class contains no reference at all to
the RandomStringsPanel class. How can this be? The mousePressed() method
in class RepaintOnClick looks at the source of the event, and calls its repaint()
method. If we have registered the RepaintOnClick object as a listener on a Ran-
domStringsPanel, then it is that panel that is repainted. But the listener object
could be used with any type of component, and it would work in the same way.
Similarly, the RandomStringsPanel class contains no reference to the RepaintOnClick
class—in fact, RandomStringsPanel was written before we even knew anything
about mouse events! The panel will send mouse events to any object that has
registered with it as a mouse listener. It does not need to know anything about
that object except that it is capable of receiving mouse events.
The relationship between an object that generates an event and an object that
responds to that event is rather loose. The relationship is set up by registering one
object to listen for events from the other object. This is something that can po-
tentially be done from outside both objects. Each object can be developed inde-
pendently, with no knowledge of the internal operation of the other object. This
is the essence of modular design: Build a complex system out of modules that
interact only in straightforward, easy to understand ways. Then each module is a
separate design problem that can be tackled independently. Java’s event-handling
framework is designed to offer strong support for modular design.
To make this clearer, let’s look at a new version of RandomStrings.java, the
program from Subsection ?? that uses RandomStringsPanel. The new version is
ClickableRandomStrings.java. For convenience, I have added RepaintOnClick as a
static nested class, although it would work just as well as a separate class:
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;

/**
* Displays a window that shows 25 copies of the string "Java !"
in
* random colors , fonts , and positions. The content of the
window
* is an object of type RandomStringsPanel . When the user clicks
* the window , the content of the window is repainted , with the
* strings in newly selected random colors , fonts , and positions.
*/
public class ClickableRandomStrings {

public static void main(String[] args) {


JFrame window = new JFrame("Click Me to Redraw!");

148
.. Mouse Events

RandomStringsPanel content = new RandomStringsPanel();


content.addMouseListener( new RepaintOnClick() );
window.setContentPane(content);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setLocation(120,70);
window.setSize(350,250);
window.setVisible(true);
}

private static class RepaintOnClick implements MouseListener


{

public void mousePressed(MouseEvent evt) {


Component source = (Component)evt.getSource();
source.repaint();
}

public void mouseClicked(MouseEvent evt) { }


public void mouseReleased(MouseEvent evt) { }
public void mouseEntered(MouseEvent evt) { }
public void mouseExited(MouseEvent evt) { }

} end class ClickableRandomStrings

MouseEvent Data

Often, when a mouse event occurs, you want to know the location of the mouse
cursor. This information is available from the MouseEvent parameter to the
event-handling method, which contains instance methods that return informa-
tion about the event. If evt is the parameter, then you can find out the coordi-
nates of the mouse cursor by calling evt.getX() and evt.getY(). These methods
return integers which give the x and y coordinates where the mouse cursor was
positioned at the time when the event occurred. The coordinates are expressed
in the coordinate system of the component that generated the event, where the
top left corner of the component is (0,0).
The user can hold down certain modifier keys while using the mouse. The
possible modifier keys include: the Shift key, the Control key, the Alt key (called
the Option key on the Mac), and the Meta key (called the Command or Apple
key on the Mac). You might want to respond to a mouse event differently when
the user is holding down a modifier key. The boolean-valued instance methods
evt.isShiftDown(), evt.isControlDown(), evt.isAltDown(), and evt.isMetaDown()
can be called to test whether the modifier keys are pressed.

149
. Graphical User Interface Programming

You might also want to have different responses depending on whether the
user presses the left mouse button, the middle mouse button, or the right mouse
button. For events triggered by a mouse button, you can determine which button
was pressed or released by calling evt.getButton(), which returns one of the inte-
ger constants MouseEvent.BUTTON1, MouseEvent.BUTTON2, or MouseEvent.BUTTON3
for the left, middle, and right buttons. For events such as mouseEntered and
mouseExited that are not triggered by buttons, evt.getButton() returns MouseEvent.NOBUTTON.
As an example, consider a JPanel that does the following: Clicking on the
panel with the left mouse button will place a red rectangle on the panel at the
point where the mouse was clicked. Clicking with the right mouse button will
place a blue oval on the panel. Holding down the Shift key while clicking will
clear the panel by removing all the shapes that have been placed. Here is what
the panel looks like after some shapes have been added:

There are several ways to write this example. There could be a separate class to
handle mouse events, as in the previous example. However, in this case, I decided
to let the panel itself respond to mouse events. Any object can be a mouse listener,
as long as it implements the MouseListener interface. In this case, the panel class
implements the MouseListener interface, so the object that represents the main
panel of the program can be the mouse listener for the program. The constructor
for the panel class contains the statement
addMouseListener(this);
which is equivalent to saying this.addMouseListener(this). Now, the ordi-
nary way to register a mouse listener is to say X.addMouseListener(Y) where Y
is the listener and X is the component that will generate the mouse events. In
the statement addMouseListener(this), both roles are played by this; that is,
“this object” (the panel) is generating mouse events and is also listening for those
events. Although this might seem a little strange, you should get used to seeing
things like this. In a large program, however, it’s usually a better idea to write a

150
.. Mouse Events

separate class (a separate stand-alone class or an inner class) to do the listening in


order to have a more organized division of responsibilities.
The source code for the panel class is shown below. I have included a main()
routine to allow the class to be run as a program, as discussed in Subsection ??.
You should check how the instance methods in the MouseEvent object are used.
You can also check for the Four Steps of Event Handling (“import java.awt.event.*”,
“implements MouseListener”, definitions for the event-handling methods, and
“addMouseListener”):
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
* A simple demonstration of MouseEvents. Shapes are drawn
* on a black background when the user clicks the panel. If
* the user Shift -clicks , the panel is cleared. If the user
* right -clicks the panel , a blue oval is drawn. Otherwise ,
* when the user clicks , a red rectangle is drawn. The contents
of
* the panel are not persistent. For example , they might
disappear
* if the panel is resized.
* This class has a main () routine to allow it to be run as an
application.
*/
public class SimpleStamper extends JPanel implements
MouseListener {

public static void main(String[] args) {


JFrame window = new JFrame("Simple Stamper");
SimpleStamper content = new SimpleStamper();
window.setContentPane(content);
window.setDefaultCloseOperation(JFrame.EXIT\_ON\_CLOSE);
window.setLocation(120,70);
window.setSize(450,350);
window.setVisible(true);
}

//
----------------------------------------------------------------------

/**
* This constructor simply sets the background color of the
panel to be black
* and sets the panel to listen for mouse events on itself.
*/

151
. Graphical User Interface Programming

public SimpleStamper() {
setBackground(Color.BLACK);
addMouseListener(this);
}

/**
* Since this panel has been set to listen for mouse events
on itself ,
* this method will be called when the user clicks the mouse
on the panel.
* This method is part of the MouseListener interface.
*/
public void mousePressed(MouseEvent evt) {

if ( evt.isShiftDown() ) {
// The user was holding down the Shift key. Just
repaint the panel.
// Since this class does not define a
paintComponent () method , the
// method from the superclass , JPanel , is called.
That method simply
// fills the panel with its background color , which
is black. The
// effect is to clear the panel.
repaint();
return;
}

int x = evt.getX(); // x-coordinate where user clicked.


int y = evt.getY(); // y-coordinate where user clicked.

Graphics g = getGraphics(); // Graphics context for


drawing directly.
// \newcode{NOTE: This is
considered to be bad
style !}

if ( evt.isMetaDown() ) {
// User right -clicked at the point (x,y). Draw a
blue oval centered
// at the point (x,y). (A black outline around the
oval will make it
// more distinct when shapes overlap .)
g.setColor(Color.BLUE); // Blue interior.
g.fillOval( x - 30, y - 15, 60, 30 );
g.setColor(Color.BLACK); // Black outline.

152
.. Mouse Events

g.drawOval( x - 30, y - 15, 60, 30 );


}
else {
// User left -clicked (or middle -clicked) at (x,y).
// Draw a red rectangle centered at (x,y).
g.setColor(Color.RED); // Red interior.
g.fillRect( x - 30, y - 15, 60, 30 );
g.setColor(Color.BLACK); // Black outline.
g.drawRect( x - 30, y - 15, 60, 30 );
}

g.dispose(); // We are finished with the graphics


context , so dispose of it.

} // end mousePressed ();

// The next four empty routines are required by the


MouseListener interface.
// They don't do anything in this class , so their definitions
are empty.

public void mouseEntered(MouseEvent evt) { }


public void mouseExited(MouseEvent evt) { }
public void mouseClicked(MouseEvent evt) { }
public void mouseReleased(MouseEvent evt) { }

} // end class SimpleStamper

Note that this class does not draw in a paintComponent() method. The rectan-
gles and ovals are drawn directly in the mousePressed() routine. To make this
possible, we obtain a graphics context by saying “g = getGraphics()”. After us-
ing g for drawing, g.dispose() is used to inform the operating system that we
will no longer be using g for drawing.

Anonymous Event Handlers and Adapter Classes

It is a fairly common practice to use anonymous inner classes to define listener


objects. As discussed in Subsection 4.3, a special form of the new operator is used
to create an object that belongs to an anonymous class. For example, a mouse
listener object can be created with an expression of the form:
new MouseListener() {
public void mousePressed(MouseEvent evt) { . . . }
public void mouseReleased(MouseEvent evt) { . . . }
public void mouseClicked(MouseEvent evt) { . . . }
public void mouseEntered(MouseEvent evt) { . . . }

153
. Graphical User Interface Programming

public void mouseExited(MouseEvent evt) { . . . }


}

This is one long expression that both defines an unnamed class and creates an
object that belongs to that class. To use the object as a mouse listener, it can be
passed as the parameter to some component’s addMouseListener() method in a
command of the form:
component.addMouseListener( new MouseListener() {
public void mousePressed(MouseEvent evt) { . . . }
public void mouseReleased(MouseEvent evt) { . . . }
public void mouseClicked(MouseEvent evt) { . . . }
public void mouseEntered(MouseEvent evt) { . . . }
public void mouseExited(MouseEvent evt) { . . . }
} );

In a typical application, most of the method definitions in this class will be empty.
A class that implements an interface must provide definitions for all the meth-
ods in that interface, even if not all methods are required. To avoid the tedium of
writing empty method definitions in cases like this, Java provides adapter classes.
An adapter class implements a listener interface by providing empty definitions
for all the methods in the interface. An adapter class is useful only as a basis for
making subclasses. In the subclass, you can define just those methods that you ac-
tually want to use by overriding the empty methods in the adapter class. For the
remaining methods, the empty definitions that are provided by the adapter class
will be used. The adapter class MouseAdapter implements both the MouseLis-
tener interface and the MouseMotionListener interface, so it can be used as a basis
for creating a listener for any mouse event. As an example, if you want a mouse
listener that only responds to mouse-pressed events, you can use a command of
the form:
component.addMouseListener( new MouseAdapter() {
public void mousePressed(MouseEvent evt) { . . . }
} );

To see how this works in a real example, let’s write another version of the Click-
ableRandomStrings program from Subsection 5.3. This version uses an anony-
mous class based on MouseAdapter to handle mouse events:
import java.awt.Component;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;

public class ClickableRandomStrings2 {

public static void main(String[] args) {

154
.. Mouse Events

JFrame window = new JFrame("Random Strings");


RandomStringsPanel content = new RandomStringsPanel();

content.addMouseListener( new MouseAdapter() {


// Register a mouse listener that is defined by an
anonymous subclass
// of MouseAdapter. This replaces the
RepaintOnClick class that was
// used in the original version.
public void mousePressed(MouseEvent evt) {
Component source = (Component)evt.getSource();
source.repaint();
}
} );

window.setContentPane(content);
window.setDefaultCloseOperation(JFrame.EXIT\_ON\_CLOSE);
window.setLocation(100,75);
window.setSize(300,240);
window.setVisible(true);
}

Anonymous inner classes can be used for other purposes besides event han-
dling. For example, suppose that you want to define a subclass of JPanel to rep-
resent a drawing surface. The subclass will only be used once. It will redefine the
paintComponent() method, but will make no other changes to JPanel. It might
make sense to define the subclass as an anonymous inner class. You will see this
pattern used in some future examples.

Timers

ot every event is generated by an action by the user. Events can also be gen-
N erated by objects as part of their regular programming, and these events can
be monitored by other objects so that they can take appropriate actions when the
events occur. One example of this is the class javax.swing.Timer. A Timer gen-
erates events at regular intervals. These events can be used to drive an animation
or to perform some other task at regular intervals. We will begin this section
with a look at timer events and animation. We will then look at another type of
basic user-generated event: the KeyEvents that are generated when the user types
on the keyboard. The example at the end of the section uses both a timer and
keyboard events to implement a simple game and introduces the important idea
of state machines.

155
. Graphical User Interface Programming

Timers and Animation

An object belonging to the class javax.swing.Timer exists only to generate events.


A Timer, by default, generates a sequence of events with a fixed delay between
each event and the next. Each event belongs to the class ActionEvent. An object
that is to listen for the events must implement the interface ActionListener, which
defines just one method:
public void actionPerformed(ActionEvent evt)
To use a Timer, you must create an object that implements the ActionListener
interface. That is, the object must belong to a class that is declared to “implement
ActionListener”, and that class must define the actionPerformed method. Then,
if the object is set to listen for events from the timer, the code in the listener’s
actionPerformed method will be executed every time the timer generates an
event.
Since there is no point to having a timer without having a listener to respond
to its events, the action listener for a timer is specified as a parameter in the
timer’s constructor. The time delay between timer events is also specified in the
constructor. If timer is a variable of type Timer, then the statement
timer = new Timer( millisDelay, listener );
creates a timer with a delay of millisDelay milliseconds between events (where
1000 milliseconds equal one second). Events from the timer are sent to the
listener. (millisDelay must be of type int, and listener must be of type
ActionListener.) The listener’s actionPerfomed() will be executed every time the
timer emits an event. Note that a timer is not guaranteed to deliver events at
precisely regular intervals. If the computer is busy with some other task, an
event might be delayed or even dropped altogether.
A timer does not automatically start generating events when the timer object
is created. The start() method in the timer must be called to tell the timer
to start running. The timer’s stop() method can be used to turn the stream of
events off. It can be restarted later by calling start() again.
One application of timers is computer animation. A computer animation is
just a sequence of still images, presented to the user one after the other. If the
time between images is short, and if the change from one image to another is
not too great, then the user perceives continuous motion. The easiest way to do
animation in Java is to use a Timer to drive the animation. Each time the timer
generates an event, the next frame of the animation is computed and drawn on
the screen—the code that implements this goes in the actionPerformed method
of an object that listens for events from the timer.
Our first example of using a timer is not exactly an animation, but it does dis-
play a new image for each timer event. The program shows randomly generated
images that vaguely resemble works of abstract art. In fact, the program draws a
new random image every time its paintComponent() method is called, and the

156
.. Mouse Events

response to a timer event is simply to call repaint(), which in turn triggers a


call to paintComponent. The work of the program is done in a subclass of JPanel,
which starts like this:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class RandomArtPanel extends JPanel {

/**
* A RepaintAction object calls the repaint method of this
panel each
* time its actionPerformed () method is called. An object of
this
* type is used as an action listener for a Timer that
generates an
* ActionEvent every four seconds. The result is that the
panel is
* redrawn every four seconds.
*/
private class RepaintAction implements ActionListener {
public void actionPerformed(ActionEvent evt) {
repaint(); // Call the repaint () method in the panel
class.
}
}

/**
* The constructor creates a timer with a delay time of four
seconds
* (4000 milliseconds), and with a RepaintAction object as its
* ActionListener. It also starts the timer running.
*/
public RandomArtPanel() {
RepaintAction action = new RepaintAction();
Timer timer = new Timer(4000, action);
timer.start();
}

/**
* The paintComponent () method fills the panel with a random
shade of
* gray and then draws one of three types of random "art".
The type
* of art to be drawn is chosen at random.
*/

157
. Graphical User Interface Programming

public void paintComponent(Graphics g) {


.
. // The rest of the class is omitted
.

You can find the full source code for this class in the file RandomArt.java. I
will only note that the very short RepaintAction class is a natural candidate to
be replaced by an anonymous inner class. That can be done where the timer is
created:
Timer timer = new timer(4000, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
repaint();
}
});

Later in this section, we will use a timer to drive the animation in a simple com-
puter game.

Keyboard Events

In Java, user actions become events in a program. These events are associated
with GUI components. When the user presses a button on the mouse, the event
that is generated is associated with the component that contains the mouse cursor.
What about keyboard events? When the user presses a key, what component is
associated with the key event that is generated?
A GUI uses the idea of input focus to determine the component associated
with keyboard events. At any given time, exactly one interface element on the
screen has the input focus, and that is where all keyboard events are directed.
If the interface element happens to be a Java component, then the information
about the keyboard event becomes a Java object of type KeyEvent, and it is de-
livered to any listener objects that are listening for KeyEvents associated with
that component. The necessity of managing input focus adds an extra twist to
working with keyboard events.
It’s a good idea to give the user some visual feedback about which component
has the input focus. For example, if the component is the typing area of a word-
processor, the feedback is usually in the form of a blinking text cursor. Another
possible visual clue is to draw a brightly colored border around the edge of a
component when it has the input focus, as I do in the examples given later in
this section.
If comp is any component, and you would like it to have the input focus, you
can call requestFocusInWindow(), which should work as long as the window
that contains the component is active and there is only one component that is
requesting focus. In some cases, when there is only one component involved,
it is enough to call this method once, just after opening the window, and the

158
.. Mouse Events

component will retain the focus for the rest of the program. (Note that there
is also a requestFocus() method that might work even when the window is
not active, but the newer method requestFocusInWindow() is preferred in most
cases.)
In a typical user interface, the user can choose to give the focus to a com-
ponent by clicking on that component with the mouse. And pressing the tab
key will often move the focus from one component to another. This is handled
automatically by the components involved, without any programming on your
part.
As our first example of processing key events, we look at a simple program
in which the user moves a square up, down, left, and right by pressing arrow
keys. When the user hits the ’R’, ’G’, ’B’, or ’K’ key, the color of the square
is set to red, green, blue, or black, respectively. Of course, none of these key
events are delivered to the panel unless it has the input focus. The panel in the
program changes its appearance when it has the input focus: When it does, a
cyan-colored border is drawn around the panel; when it does not, a gray-colored
border is drawn. The complete source code for this example can be found in the
file KeyboardAndFocusDemo.java. I will discuss some aspects of it below. After
reading this section, you should be able to understand the source code in its
entirety.
In Java, keyboard event objects belong to a class called KeyEvent. An object
that needs to listen for KeyEvents must implement the interface named KeyLis-
tener. Furthermore, the object must be registered with a component by calling
the component’s addKeyListener() method. The registration is done with the
command “component.addKeyListener(listener);” where listener is the ob-
ject that is to listen for key events, and component is the object that will generate
the key events (when it has the input focus). It is possible for component and
listener to be the same object. All this is, of course, directly analogous to what
you learned about mouse events in the previous section. The KeyListener inter-
face defines the following methods, which must be included in any class that
implements KeyListener:
public void keyPressed(KeyEvent evt);
public void keyReleased(KeyEvent evt);
public void keyTyped(KeyEvent evt);

Java makes a distinction between the keys that you press and the characters
that you type. There are lots of keys on a keyboard: letter keys, number keys,
modifier keys such as Control and Shift, arrow keys, page up and page down
keys, keypad keys, function keys, and so on. In some cases, such as the shift key,
pressing a key does not type a character. On the other hand, typing a character
sometimes involves pressing several keys. For example, to type an uppercase ’A’,
you have to press the Shift key and then press the A key before releasing the

159
. Graphical User Interface Programming

Shift key. On my Mac OS computer, I can type an accented e, by holding down


the Option key, pressing the E key, releasing the Option key, and pressing E
again. Only one character was typed, but I had to perform three key-presses
and I had to release a key at the right time. In Java, there are three types of
KeyEvent. The types correspond to pressing a key, releasing a key, and typing
a character. The keyPressed method is called when the user presses a key, the
keyReleased method is called when the user releases a key, and the keyTyped
method is called when the user types a character (whether that’s done with one
key press or several). Note that one user action, such as pressing the E key, can
be responsible for two events, a keyPressed event and a keyTyped event. Typing
an upper case ’A’ can generate two keyPressed events, two keyReleased events,
and one keyTyped event.
Usually, it is better to think in terms of two separate streams of events, one
consisting of keyPressed and keyReleased events and the other consisting of
keyTyped events. For some applications, you want to monitor the first stream; for
other applications, you want to monitor the second one. Of course, the informa-
tion in the keyTyped stream could be extracted from the keyPressed/keyReleased
stream, but it would be difficult (and also system-dependent to some extent).
Some user actions, such as pressing the Shift key, can only be detected as keyPressed
events. I used to have a computer solitaire game that highlighted every card that
could be moved, when I held down the Shift key. You can do something like
that in Java by hiliting the cards when the Shift key is pressed and removing the
highlight when the Shift key is released.
There is one more complication. Usually, when you hold down a key on the
keyboard, that key will auto-repeat. This means that it will generate multiple
keyPressed events with just one keyReleased at the end of the sequence. It can
also generate multiple keyTyped events. For the most part, this will not affect
your programming, but you should not expect every keyPressed event to have a
corresponding keyReleased event.
Every key on the keyboard has an integer code number. When the keyPressed
or keyReleased method is called, the parameter, evt, contains the code of the
key that was pressed or released. The code can be obtained by calling the function
evt.getKeyCode(). Rather than asking you to memorize a table of code numbers,
Java provides a named constant for each key. These constants are defined in the
KeyEvent class. For example the constant for the shift key is KeyEvent.VK_SHIFT.
If you want to test whether the key that the user pressed is the Shift key, you could
say “if (evt.getKeyCode()== KeyEvent.VK_SHIFT)”. The key codes for the
four arrow keys are KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT, KeyEvent.VK_UP,
and KeyEvent.VK_DOWN. Other keys have similar codes. (The “VK” stands for
“Virtual Keyboard”. In reality, different keyboards use different key codes, but
Java translates the actual codes from the keyboard into its own “virtual” codes.
Your program only sees these virtual key codes, so it will work with various key-

160
.. Mouse Events

boards on various platforms without modification.)


In the case of a keyTyped event, you want to know which character was typed.
This information can be obtained from the parameter, evt, in the keyTyped
method by calling the function evt.getKeyChar(). This function returns a value
of type char representing the character that was typed.
In the KeyboardAndFocusDemo program, I use the keyPressed routine to re-
spond when the user presses one of the arrow keys. The program includes in-
stance variables, squareLeft and squareTop, that give the position of the up-
per left corner of the movable square. When the user presses one of the arrow
keys, the keyPressed routine modifies the appropriate instance variable and calls
repaint() to redraw the panel with the square in its new position. Note that the
values of squareLeft and squareTop are restricted so that the square never moves
outside the white area of the panel:
/**
* This is called each time the user presses a key while the
panel has
* the input focus. If the key pressed was one of the arrow
keys ,
* the square is moved (except that it is not allowed to move
off the
* edge of the panel , allowing for a 3-pixel border).
*/
public void keyPressed(KeyEvent evt) {

int key = evt.getKeyCode(); // keyboard code for the pressed


key

if (key == KeyEvent.VK_LEFT) { // left -arrow key; move the


square left
squareLeft -= 8;
if (squareLeft < 3)
squareLeft = 3;
repaint();
}
else if (key == KeyEvent.VK_RIGHT) { // right -arrow key;
move the square right
squareLeft += 8;
if (squareLeft > getWidth() - 3 - SQUARE_SIZE)
squareLeft = getWidth() - 3 - SQUARE_SIZE;
repaint();
}
else if (key == KeyEvent.VK_UP) { // up -arrow key; move the
square up
squareTop -= 8;
if (squareTop < 3)

161
. Graphical User Interface Programming

squareTop = 3;
repaint();
}
else if (key == KeyEvent.VK_DOWN) { // down -arrow key; move
the square down
squareTop += 8;
if (squareTop > getHeight() - 3 - SQUARE_SIZE)
squareTop = getHeight() - 3 - SQUARE_SIZE;
repaint();
}

} // end keyPressed ()

Color changes—which happen when the user types the characters ’R’, ’G’,
’B’, and ’K’, or the lower case equivalents—are handled in the keyTyped method.
I won’t include it here, since it is so similar to the keyPressed method. Finally,
to complete the KeyListener interface, the keyReleased method must be defined.
In the sample program, the body of this method is empty since the program does
nothing in response to keyReleased events.

5.4 Basic Components

n preceding sections, you’ve seen how to use a graphics context to draw on


I the screen and how to handle mouse events and keyboard events. In one
sense, that’s all there is to GUI programming. If you’re willing to program all
the drawing and handle all the mouse and keyboard events, you have nothing
more to learn. However, you would either be doing a lot more work than you
need to do, or you would be limiting yourself to very simple user interfaces. A
typical user interface uses standard GUI components such as buttons, scroll bars,
text-input boxes, and menus. These components have already been written for
you, so you don’t have to duplicate the work involved in developing them. They
know how to draw themselves, and they can handle the details of processing the
mouse and keyboard events that concern them.
Consider one of the simplest user interface components, a push button. The
button has a border, and it displays some text. This text can be changed. Some-
times the button is disabled, so that clicking on it doesn’t have any effect. When
it is disabled, its appearance changes. When the user clicks on the push button,
the button changes appearance while the mouse button is pressed and changes
back when the mouse button is released. In fact, it’s more complicated than
that. If the user moves the mouse outside the push button before releasing the
mouse button, the button changes to its regular appearance. To implement this,
it is necessary to respond to mouse exit or mouse drag events. Furthermore, on
many platforms, a button can receive the input focus. The button changes ap-

162
.. Basic Components

pearance when it has the focus. If the button has the focus and the user presses
the space bar, the button is triggered. This means that the button must respond
to keyboard and focus events as well.
Fortunately, you don’t have to program any of this, provided you use an object
belonging to the standard class javax.swing.JButton. A JButton object draws
itself and processes mouse, keyboard, and focus events on its own. You only hear
from the JButton when the user triggers it by clicking on it or pressing the space
bar while the button has the input focus. When this happens, the JButton object
creates an event object belonging to the class java.awt.event.ActionEvent. The
event object is sent to any registered listeners to tell them that the button has been
pushed. Your program gets only the information it needs—the fact that a button
was pushed.
∗ ∗ ∗
The standard components that are defined as part of the Swing graphical user
interface API are defined by subclasses of the class JComponent, which is itself a
subclass of Component. Many useful methods are defined in the Component and
JComponent classes and so can be used with any Swing component. We begin by
looking at a few of these methods. Suppose that comp is a variable that refers to
some JComponent. Then the following methods can be used:
• comp.getWidth() and comp.getHeight() are functions that give the current
size of the component, in pixels. One warning: When a component is
first created, its size is zero. The size will be set later, probably by a layout
manager. A common mistake is to check the size of a component before
that size has been set, such as in a constructor.
• comp.setEnabled(true) and comp.setEnabled(false) can be used to en-
able and disable the component. When a component is disabled, its ap-
pearance might change, and the user cannot do anything with it. There is
a boolean-valued function, comp.isEnabled() that you can call to discover
whether the component is enabled.
• comp.setVisible(true) and comp.setVisible(false) can be called to hide
or show the component.
• comp.setFont(font) sets the font that is used for text displayed on the
component. See Subsection 5.2 for a discussion of fonts.
• comp.setBackground(color) and comp.setForeground(color) set the back-
ground and foreground colors for the component. See Subsection 5.2.
• comp.setOpaque(true) tells the component that the area occupied by the
component should be filled with the component’s background color be-
fore the content of the component is painted. By default, only JLabels are
non-opaque. A non-opaque, or “transparent”, component ignores its back-

163
. Graphical User Interface Programming

ground color and simply paints its content over the content of its container.
This usually means that it inherits the background color from its container.
• comp.setToolTipText(string) sets the specified string as a “tool tip” for
the component. The tool tip is displayed if the mouse cursor is in the com-
ponent and the mouse is not moved for a few seconds. The tool tip should
give some information about the meaning of the component or how to use
it.
• comp.setPreferredSize(size) sets the size at which the component should
be displayed, if possible. The parameter is of type java.awt.Dimension,
where an object of type Dimension has two public integer-valued instance
variables, width and height. A call to this method usually looks something
like “setPreferredSize( new Dimension(100,50)~)”. The preferred size
is used as a hint by layout managers, but will not be respected in all cases.
Standard components generally compute a correct preferred size automati-
cally, but it can be useful to set it in some cases. For example, if you use a
JPanel as a drawing surface, it is usually a good idea to set a preferred size
for it, since its default preferred size is zero.
Note that using any component is a multi-step process. The component ob-
ject must be created with a constructor. It must be added to a container. In
many cases, a listener must be registered to respond to events from the compo-
nent. And in some cases, a reference to the component must be saved in an
instance variable so that the component can be manipulated by the program af-
ter it has been created. In this section, we will look at a few of the basic standard
components that are available in Swing. In the next section we will consider the
problem of laying out components in containers.

JButton

An object of class JButton is a push button that the user can click to trigger some
action. You’ve already seen buttons used in Section 5.1, but we consider them
in much more detail here. To use any component effectively, there are several
aspects of the corresponding class that you should be familiar with. For JButton,
as an example, I list these aspects explicitly:
• Constructors: The JButton class has a constructor that takes a string as a
parameter. This string becomes the text displayed on the button. For ex-
ample: stopGoButton = new~JButton("Go"). This creates a button object
that will display the text, “Go” (but remember that the button must still be
added to a container before it can appear on the screen).
• Events: When the user clicks on a button, the button generates an event of
type ActionEvent. This event is sent to any listener that has been registered
with the button as an ActionListener.

164
.. Basic Components

• Listeners: An object that wants to handle events generated by buttons


must implement the ActionListener interface. This interface defines just
one method, “public void actionPerformed(ActionEvent evt)”, which
is called to notify the object of an action event.
• Registration of Listeners: In order to actually receive notification of an
event from a button, an ActionListener must be registered with the button.
This is done with the button’s addActionListener() method. For example:
stopGoButton.addActionListener( buttonHandler~);
• Event methods: When actionPerformed(evt) is called by the button, the
parameter, evt, contains information about the event. This information
can be retrieved by calling methods in the ActionEvent class. In particular,
evt.getActionCommand() returns a String giving the command associated
with the button. By default, this command is the text that is displayed on
the button, but it is possible to set it to some other string. The method
evt.getSource() returns a reference to the object that produced the event,
that is, to the JButton that was pressed. The return value is of type Object,
not JButton, because other types of components can also produce Action-
Events.
• Component methods: Several useful methods are defined in the JButton
class, in addition to the standard Component methods. For example,
stopGoButton.setText("Stop") changes the text displayed on the button
to “Stop”. And stopGoButton.setActionCommand("sgb") changes the ac-
tion command associated with this button for action events. The setEnabled()
and setText() methods are particularly useful for giving the user informa-
tion about what is going on in the program. A disabled button is better
than a button that gives an obnoxious error message such as “Sorry, you
can’t click on me now!”

JLabel

JLabel is certainly the simplest type of component. An object of type JLabel exists
just to display a line of text. The text cannot be edited by the user, although it
can be changed by your program. The constructor for a JLabel specifies the text
to be displayed:
JLabel message = new JLabel("Hello World!");
There is another constructor that specifies where in the label the text is located, if
there is extra space. The possible alignments are given by the constants JLabel.LEFT,
JLabel.CENTER, and JLabel.RIGHT. For example,
JLabel message = new JLabel("Hello World!", JLabel.CENTER);
creates a label whose text is centered in the available space. You can change the
text displayed in a label by calling the label’s setText() method:

165
. Graphical User Interface Programming

message.setText("Goodbye World!");
Since the JLabel class is a subclass of JComponent, you can use methods such
as setForeground() and setFont() with labels. If you want the background
color to have any effect, you should call setOpaque(true) on the label, since
otherwise the JLabel might not fill in its background. For example:
JLabel message = new JLabel("Hello World!", JLabel.CENTER);
message.setForeground(Color.RED); // Display red text ...
message.setBackground(Color.BLACK); // on a black
background ...
message.setFont(new Font("Serif",Font.BOLD,18)); // in a big
bold font.
message.setOpaque(true); // Make sure background is filled in.

JCheckBox

A JCheckBox is a component that has two states: selected or unselected. The user
can change the state of a check box by clicking on it. The state of a checkbox is
represented by a boolean value that is true if the box is selected and is false if
the box is unselected. A checkbox has a label, which is specified when the box is
constructed:
JCheckBox showTime = new JCheckBox("Show Current Time");
Usually, it’s the user who sets the state of a JCheckBox, but you can also
set the state programmatically. The current state of a checkbox is set using its
setSelected(boolean) method. For example, if you want the checkbox showTime
to be checked, you would say “showTime.setSelected(true)". To uncheck the
box, say “showTime.setSelected(false)". You can determine the current state
of a checkbox by calling its isSelected() method, which returns a boolean value.
In many cases, you don’t need to worry about events from checkboxes. Your
program can just check the state whenever it needs to know it by calling the
isSelected() method. However, a checkbox does generate an event when its
state is changed by the user, and you can detect this event and respond to it if
you want something to happen at the moment the state changes. When the state
of a checkbox is changed by the user, it generates an event of type ActionEvent.
If you want something to happen when the user changes the state, you must
register an ActionListener with the checkbox by calling its addActionListener()
method. (Note that if you change the state by calling the setSelected() method,
no ActionEvent is generated. However, there is another method in the JCheckBox
class, doClick(), which simulates a user click on the checkbox and does generate
an ActionEvent.)
When handling an ActionEvent, you can call evt.getSource() in the actionPerformed()
method to find out which object generated the event. (Of course, if you are only
listening for events from one component, you don’t have to do this.) The re-

166
.. Basic Components

turned value is of type Object, but you can type-cast it to another type if you
want. Once you know the object that generated the event, you can ask the object
to tell you its current state. For example, if you know that the event had to come
from one of two checkboxes, cb1 or cb2, then your actionPerformed() method
might look like this:

public void actionPerformed(ActionEvent evt) {


Object source = evt.getSource();
if (source == cb1) {
boolean newState = cb1.isSelected();
... // respond to the change of state
}
else if (source == cb2) {
boolean newState = cb2.isSelected();
... // respond to the change of state
}
}

Alternatively, you can use evt.getActionCommand() to retrieve the action


command associated with the source. For a JCheckBox, the action command
is, by default, the label of the checkbox.

JTextField and JTextArea

The JTextField and JTextArea classes represent components that contain text that
can be edited by the user. A JTextField holds a single line of text, while a JTextArea
can hold multiple lines. It is also possible to set a JTextField or JTextArea to
be read-only so that the user can read the text that it contains but cannot edit
the text. Both classes are subclasses of an abstract class, JTextComponent, which
defines their common properties.
JTextField and JTextArea have many methods in common. The instance
method setText(), which takes a parameter of type String, can be used to change
the text that is displayed in an input component. The contents of the component
can be retrieved by calling its getText() instance method, which returns a value
of type String. If you want to stop the user from modifying the text, you can call
setEditable(false). Call the same method with a parameter of true to make
the input component user-editable again.
The user can only type into a text component when it has the input focus. The
user can give the input focus to a text component by clicking it with the mouse,
but sometimes it is useful to give the input focus to a text field programmatically.
You can do this by calling its requestFocusInWindow() method. For example,
when I discover an error in the user’s input, I usually call requestFocusInWindow()
on the text field that contains the error. This helps the user see where the error
occurred and lets the user start typing the correction immediately.

167
. Graphical User Interface Programming

By default, there is no space between the text in a text component and the
edge of the component, which usually doesn’t look very good. You can use the
setMargin() method of the component to add some blank space between the
edge of the component and the text. This method takes a parameter of type
java.awt.Insets which contains four integer instance variables that specify the
margins on the top, left, bottom, and right edge of the component. For example,
textComponent.setMargin( new Insets(5,5,5,5));
adds a five-pixel margin between the text in textComponent and each edge of the
component.
∗ ∗ ∗
The JTextField class has a constructor
public JTextField(int columns)
where columns is an integer that specifies the number of characters that should
be visible in the text field. This is used to determine the preferred width of the
text field. (Because characters can be of different sizes and because the preferred
width is not always respected, the actual number of characters visible in the text
field might not be equal to columns.) You don’t have to specify the number of
columns; for example, you might use the text field in a context where it will
expand to fill whatever space is available. In that case, you can use the default
constructor JTextField(), with no parameters. You can also use the following
constructors, which specify the initial contents of the text field:
public JTextField(String contents);
public JTextField(String contents, int columns);

The constructors for a JTextArea are


public JTextArea()
public JTextArea(int rows, int columns)
public JTextArea(String contents)
public JTextArea(String contents, int rows, int columns)

The parameter rows specifies how many lines of text should be visible in the text
area. This determines the preferred height of the text area, just as columns de-
termines the preferred width. However, the text area can actually contain any
number of lines; the text area can be scrolled to reveal lines that are not currently
visible. It is common to use a JTextArea as the CENTER component of a Border-
Layout. In that case, it is less useful to specify the number of lines and columns,
since the TextArea will expand to fill all the space available in the center area of
the container.
The JTextArea class adds a few useful methods to those inherited from JTextCom-
ponent. For example, the instance method append(moreText), where moreText
is of type String, adds the specified text at the end of the current content of the
text area. (When using append() or setText() to add text to a JTextArea, line

168
.. Basic Layout

breaks can be inserted in the text by using the newline character, '\1n'.) And
setLineWrap(wrap), where wrap is of type boolean, tells what should happen
when a line of text is too long to be displayed in the text area. If wrap is true,
then any line that is too long will be “wrapped” onto the next line; if wrap is false,
the line will simply extend outside the text area, and the user will have to scroll
the text area horizontally to see the entire line. The default value of wrap is false.
Since it might be necessary to scroll a text area to see all the text that it con-
tains, you might expect a text area to come with scroll bars. Unfortunately, this
does not happen automatically. To get scroll bars for a text area, you have to put
the JTextArea inside another component, called a JScrollPane. This can be done
as follows:
JTextArea inputArea = new JTextArea();
JScrollPane scroller = new JScrollPane( inputArea );

The scroll pane provides scroll bars that can be used to scroll the text in the text
area. The scroll bars will appear only when needed, that is when the size of the
text exceeds the size of the text area. Note that when you want to put the text
area into a container, you should add the scroll pane, not the text area itself, to
the container. See the program TextAreaDemo.java for a very short example of
using a text area in a scroll pane.
∗ ∗ ∗
When the user is typing in a JTextField and presses return, an ActionEvent
is generated. If you want to respond to such events, you can register an Action-
Listener with the text field, using the text field’s addActionListener() method.
(Since a JTextArea can contain multiple lines of text, pressing return in a text area
does not generate an event; it simply begins a new line of text.)
JTextField has a subclass, JPasswordField, which is identical except that it does
not reveal the text that it contains. The characters in a JPasswordField are all dis-
played as asterisks (or some other fixed character). A password field is, obviously,
designed to let the user enter a password without showing that password on the
screen.
Text components are actually quite complex, and I have covered only their
most basic properties here. I will return to the topic of text components in Chap-
ter ??.

5.5 Basic Layout

omponents are the fundamental building blocks of a graphical user interface.


C But you have to do more with components besides create them. Another
aspect of GUI programming is laying out components on the screen, that is,
deciding where they are drawn and how big they are. You have probably noticed

169
. Graphical User Interface Programming

that computing coordinates can be a difficult problem, especially if you don’t


assume a fixed size for the drawing area. Java has a solution for this, as well.
Components are the visible objects that make up a GUI. Some components
are containers, which can hold other components. Containers in Java are objects
that belong to some subclass of java.awt.Container. The content pane of a
JFrame is an example of a container. The standard class JPanel, which we have
mostly used as a drawing surface up until now, is another example of a container.
Because a JPanel object is a container, it can hold other components. Because
a JPanel is itself a component, you can add a JPanel to another JPanel. This
makes complex nesting of components possible. JPanels can be used to organize
complicated user interfaces, as shown in this illustration:

In this picture, a large panel holds two smaller panels. Each of the two smaller
panels in turn holds three components.
The components in a container must be “laid out,” which means setting their
sizes and positions. It’s possible to program the layout yourself, but layout is
ordinarily done by a layout manager. A layout manager is an object associated
with a container that implements some policy for laying out the components in
that container. Different types of layout manager implement different policies.
In this section, we will cover the three most common types of layout manager,
and then we will look at several programming examples that use components and
layout.
Every container has a default layout manager and has an instance method,
setLayout(), that takes a parameter of type LayoutManager and that is used to
specify a different layout manager for the container. Components are added to
a container by calling an instance method named add() in the container object.
There are actually several versions of the add() method, with different parameter
lists. Different versions of add() are appropriate for different layout managers,
as we will see below.

170
.. Basic Layout

Basic Layout Managers

Java has a variety of standard layout managers that can be used as parameters in
the setLayout() method. They are defined by classes in the package java.awt.
Here, we will look at just three of these layout manager classes: FlowLayout, Bor-
derLayout, and GridLayout.
A FlowLayout simply lines up components in a row across the container. The
size of each component is equal to that component’s “preferred size.” After laying
out as many items as will fit in a row across the container, the layout manager
will move on to the next row. The default layout for a JPanel is a FlowLayout;
that is, a JPanel uses a FlowLayout unless you specify a different layout manager
by calling the panel’s setLayout() method.
The components in a given row can be either left-aligned, right-aligned, or
centered within that row, and there can be horizontal and vertical gaps between
components. If the default constructor, “new FlowLayout()”, is used, then the
components on each row will be centered and both the horizontal and the vertical
gaps will be five pixels. The constructor
public FlowLayout(int align, int hgap, int vgap)
can be used to specify alternative alignment and gaps. The possible values of
align are FlowLayout.LEFT, FlowLayout.RIGHT, and FlowLayout.CENTER.
Suppose that container is a container object that is using a FlowLayout as its
layout manager. Then, a component, comp, can be added to the container with
the statement
container.add(comp);
The FlowLayout will line up all the components that have been added to the
container in this way. They will be lined up in the order in which they were added.
For example, this picture shows five buttons in a panel that uses a FlowLayout:

Note that since the five buttons will not fit in a single row across the panel, they
are arranged in two rows. In each row, the buttons are grouped together and are
centered in the row. The buttons were added to the panel using the statements:
panel.add(button1);
panel.add(button2);
panel.add(button3);
panel.add(button4);
panel.add(button5);

171
. Graphical User Interface Programming

When a container uses a layout manager, the layout manager is ordinarily


responsible for computing the preferred size of the container (although a different
preferred size could be set by calling the container’s setPreferredSize method).
A FlowLayout prefers to put its components in a single row, so the preferred width
is the total of the preferred widths of all the components, plus the horizontal gaps
between the components. The preferred height is the maximum preferred height
of all the components.
∗ ∗ ∗
A BorderLayout layout manager is designed to display one large, central com-
ponent, with up to four smaller components arranged around the edges of the
central component. If a container, cntr, is using a BorderLayout, then a compo-
nent, comp, should be added to the container using a statement of the form
cntr.add( comp, borderLayoutPosition );
where borderLayoutPosition specifies what position the component should oc-
cupy in the layout and is given as one of the constants BorderLayout.CENTER,
BorderLayout.NORTH, BorderLayout.SOUTH, BorderLayout.EAST, or BorderLayout.WEST.
The meaning of the five positions is shown in this diagram:

North
West

East

Center

South

Note that a border layout can contain fewer than five components, so that not all
five of the possible positions need to be filled. It would be very unusual, however,
to have no center component.
A BorderLayout sets the sizes of its components as follows: The NORTH and
SOUTH components (if present) are shown at their preferred heights, but their
width is set equal to the full width of the container. The EAST and WEST compo-
nents are shown at their preferred widths, but their height is set to the height of
the container, minus the space occupied by the NORTH and SOUTH components.
Finally, the CENTER component takes up any remaining space. The preferred size
of the CENTER component is ignored when the layout is done, but it is taken into
account when the preferred size of the container as a whole is computed. You
should make sure that the components that you put into a BorderLayout are suit-
able for the positions that they will occupy. A horizontal slider or text field, for
example, would work well in the NORTH or SOUTH position, but wouldn’t make
much sense in the EAST or WEST position.

172
.. Basic Layout

The default constructor, new BorderLayout(), leaves no space between com-


ponents. If you would like to leave some space, you can specify horizontal and
vertical gaps in the constructor of the BorderLayout object. For example, if you
say
panel.setLayout(new BorderLayout(5,7));
then the layout manager will insert horizontal gaps of 5 pixels between compo-
nents and vertical gaps of 7 pixels between components. The background color
of the container will show through in these gaps. The default layout for the origi-
nal content pane that comes with a JFrame is a BorderLayout with no horizontal
or vertical gap.
∗ ∗ ∗
Finally, we consider the GridLayout layout manager. A grid layout lays out
components in a grid containing rows and columns of equal sized rectangles.
This illustration shows how the components would be arranged in a grid layout
with 4 rows and 3 columns:

#1 #2 #3
#4 #5 #6
#7 #8 #9
#10 #11 #12

If a container uses a GridLayout, the appropriate add method for the container
takes a single parameter of type Component (for example: cntr.add(comp)). Com-
ponents are added to the grid in the order shown; that is, each row is filled from
left to right before going on the next row.
The constructor for a GridLayout takes the form “new GridLayout(R,C)”,
where R is the number of rows and C is the number of columns. If you want
to leave horizontal gaps of H pixels between columns and vertical gaps of V pixels
between rows, use “new GridLayout(R,C,H,V)” instead.
When you use a GridLayout, it’s probably good form to add just enough com-
ponents to fill the grid. However, this is not required. In fact, as long as you
specify a non-zero value for the number of rows, then the number of columns is
essentially ignored. The system will use just as many columns as are necessary to
hold all the components that you add to the container. If you want to depend on
this behavior, you should probably specify zero as the number of columns. You
can also specify the number of rows as zero. In that case, you must give a non-
zero number of columns. The system will use the specified number of columns,
with just as many rows as necessary to hold the components that are added to
the container.

173
. Graphical User Interface Programming

Horizontal grids, with a single row, and vertical grids, with a single column,
are very common. For example, suppose that button1, button2, and button3
are buttons and that you’d like to display them in a horizontal row in a panel. If
you use a horizontal grid for the panel, then the buttons will completely fill that
panel and will all be the same size. The panel can be created as follows:
JPanel buttonBar = new JPanel();
buttonBar.setLayout( new GridLayout(1,3) );
// (Note: The "3" here is pretty much ignored , and
// you could also say "new GridLayout (1,0)".
// To leave gaps between the buttons , you could use
// "new GridLayout (1,0,5,5) ".)
buttonBar.add(button1);
buttonBar.add(button2);
buttonBar.add(button3);

You might find this button bar to be more attractive than the one that uses the
default FlowLayout layout manager.

5.6 A Simple Calculator

As our next example, we look briefly at an example that uses nested subpanels to
build a more complex user interface. The program has two JTextFields where the
user can enter two numbers, four JButtons that the user can click to add, subtract,
multiply, or divide the two numbers, and a JLabel that displays the result of the
operation. Here is a picture from the program:

This example uses a panel with a GridLayout that has four rows and one column.
In this case, the layout is created with the statement:
setLayout(new GridLayout(4,1,3,3));
which allows a 3-pixel gap between the rows where the gray background color of
the panel is visible.
The first row of the grid layout actually contains two components, a JLabel
displaying the text “x~=” and a JTextField. A grid layout can only have one com-
ponent in each position. In this case, the component in the first row is a JPanel,
a subpanel that is nested inside the main panel. This subpanel in turn contains
the label and text field. This can be programmed as follows:

174
.. A Simple Calculator

xInput = new JTextField("0", 10); // Create a text field sized


to hold 10 chars.
JPanel xPanel = new JPanel(); // Create the subpanel.
xPanel.add( new JLabel(" x = ")); // Add a label to the subpanel.
xPanel.add(xInput); // Add the text field to the
subpanel

add(xPanel); // Add the subpanel to the


main panel.

The subpanel uses the default FlowLayout layout manager, so the label and text
field are simply placed next to each other in the subpanel at their preferred size,
and are centered in the subpanel.
Similarly, the third row of the grid layout is a subpanel that contains four
buttons. In this case, the subpanel uses a GridLayout with one row and four
columns, so that the buttons are all the same size and completely fill the subpanel.
One other point of interest in this example is the actionPerformed() method
that responds when the user clicks one of the buttons. This method must retrieve
the user’s numbers from the text field, perform the appropriate arithmetic oper-
ation on them (depending on which button was clicked), and set the text of the
JLabel (named answer) to represent the result. However, the contents of the text
fields can only be retrieved as strings, and these strings must be converted into
numbers. If the conversion fails, the label is set to display an error message:
public void actionPerformed(ActionEvent evt) {

double x, y; // The numbers from the input boxes.

try {
String xStr = xInput.getText();
x = Double.parseDouble(xStr);
}
catch (NumberFormatException e) {
// The string xStr is not a legal number.
answer.setText("Illegal data for x.");
xInput.requestFocusInWindow();
return;
}

try {
String yStr = yInput.getText();
y = Double.parseDouble(yStr);
}
catch (NumberFormatException e) {
// The string yStr is not a legal number.
answer.setText("Illegal data for y.");
yInput.requestFocusInWindow();

175
. Graphical User Interface Programming

return;
}

/* Perform the operation based on the action command from the


button. The action command is the text displayed on the
button.
Note that division by zero produces an error message. */

String op = evt.getActionCommand();
if (op.equals("+"))
answer.setText( "x + y = " + (x+y) );
else if (op.equals("-"))
answer.setText( "x - y = " + (x-y) );
else if (op.equals("*"))
answer.setText( "x * y = " + (x*y) );
else if (op.equals("/")) {
if (y == 0)
answer.setText("Can't divide by zero!");
else
answer.setText( "x / y = " + (x/y) );
}

} // end actionPerformed ()

The complete source code for this example can be found in SimpleCalc.java.

176
Exercises

Exercises for Chapter 5

1. In the SimpleStamper example from Subsection 5.3, a rectangle or oval is


drawn on the panel when the user clicks the mouse. Except, when the
user shift-clicks, the panel is cleared instead. Modify this class so that the
modified version will continue to draw figures as the user drags the mouse.
That is, the mouse will leave a trail of figures as the user drags. However,
if the user shift-clicks, the panel should simply be cleared and no figures
should be drawn even if the user drags the mouse after shift-clicking. Here
is a picture of my solution:

The source code for the original program is SimpleStamper.java. See


the discussion of dragging in Subsection ??. (Note that the original version
uses a black background, with a black border around each shape. That
didn’t work well with a lot of closely spaced shapes, so the new version uses
a white background.)
If you want to make the problem a little more challenging, when draw-
ing shapes during a drag operation, make sure that the shapes that are drawn
are at least, say, 5 pixels apart. To implement this, you have to keep track
of the position of the last shape that was drawn.

2. Write a program that shows a small red square and a small blue square. The
user should be able to drag either square with the mouse. (You’ll need an
instance variable to remember which square the user is dragging.) The user
can drag the square out of the panel if she wants; if she does this, there is
no way to get it back.
Note that for this exercise, you should do all the drawing in the paintComponent()
method (as indeed you should whenever possible).

177
. Graphical User Interface Programming

3. Write a program that shows a pair of dice. When the user clicks on the
panel in the program, the dice should be rolled (that is, the dice should be
assigned newly computed random values). Each die should be drawn as a
square showing from 1 to 6 dots. Since you have to draw two dice, its a
good idea to write a method, “void drawDie(Graphics g, int val, int
x, int y)”, to draw a die at the specified (x,y) coordinates. The second
parameter, val, specifies the value that is showing on the die. Assume that
the size of the panel is 100 by 100 pixels. Here is a picture of the panel that
displays the dice:

4. In Exercise 6.3, you wrote a pair-of-dice panel where the dice are rolled
when the user clicks on the panel. Now make a pair-of-dice program in
which the user rolls the dice by clicking a button. The button should ap-
pear under the panel that shows the dice. Also make the following change:
When the dice are rolled, instead of just showing the new value, show a
short animation during which the values on the dice are changed in every
frame. The animation is supposed to make the dice look more like they are
actually rolling.

5. In Exercise 3.8, you drew a checkerboard. For this exercise, write a program
where the user can select a square by clicking on it. (Use a JPanel for the
checkerboard.) Highlight the selected square by drawing a colored border
around it. When the program starts, no square is selected. When the user
clicks on a square that is not currently selected, it becomes selected (and the
previously selected square, if any, is unselected). If the user clicks the square
that is selected, it becomes unselected. Assume that the size of the panel is
exactly 160 by 160 pixels, so that each square on the checkerboard is 20 by
20 pixels. Here is my checkerboard, with the square in row 3, column 3
selected:

178
Exercises

6. Write a program that has a JTextArea where the user can enter some text.
Then program should have a button such that when the user clicks on the
button, the panel will count the number of lines in the user’s input, the
number of words in the user’s input, and the number of characters in the
user’s input. This information should be displayed on three labels. Re-
call that if textInput is a JTextArea, then you can get the contents of the
JTextArea by calling the function textInput.getText(). This function re-
turns a String containing all the text from the text area. The number of
characters is just the length of this String. Lines in the String are separated
by the new line character, '\1n', so the number of lines is just the number
of new line characters in the String, plus one. Words are a little harder to
count. Exercise 3.4 has some advice about finding the words in a String.
Essentially, you want to count the number of characters that are first char-
acters in words. Don’t forget to put your JTextArea in a JScrollPane, and add
the scroll pane to the container, not the text area. Scrollbars should appear
when the user types more text than will fit in the available area. Here is a
picture of my solution:

179
. Graphical User Interface Programming

7. A polygon is a geometric figure made up of a sequence of connected line


segments. The points where the line segments meet are called the vertices
of the polygon. The Graphics class includes commands for drawing and
filling polygons. For these commands, the coordinates of the vertices of the
polygon are stored in arrays. If g is a variable of type Graphics then
• g.drawPolygon(xCoords, yCoords, pointCt) will draw the outline
of the polygon with vertices at the points (xCoords[0],yCoords[0]),
(xCoords[1],yCoords[1]), …, (xCoords[pointCt-1],yCoords[pointCt-1]).
The third parameter, pointCt, is an int that specifies the number of ver-
tices of the polygon. Its value should be 3 or greater. The first two pa-
rameters are arrays of type int[]. Note that the polygon automatically
includes a line from the last point, (xCoords[pointCt-1],yCoords[pointCt-1]),
back to the starting point (xCoords[0],yCoords[0]).
• g.fillPolygon(xCoords, yCoords, pointCt) fills the interior of the
polygon with the current drawing color. The parameters have the same
meaning as in the drawPolygon() method. Note that it is OK for the
sides of the polygon to cross each other, but the interior of a polygon
with self-intersections might not be exactly what you expect.
Write a program that lets the user draw polygons. As the user clicks a
sequence of points, count them and store their x- and y-coordinates in two
arrays. These points will be the vertices of the polygon. As the user is creat-
ing the polygon, you should just connect all the points with line segments.
When the user clicks near the starting point, draw the complete polygon.
Draw it with a red interior and a black border. Once the user has completed

180
Exercises

a polygon, the next click will clear the data and start a new polygon from
scratch. All drawing should be done in the paintComponent() method.
Here is a picture of my solution after the user has drawn a fairly complex
polygon:

8. Write a GUI Blackjack program that lets the user play a game of Blackjack,
with the computer as the dealer. The program should draw the user’s cards
and the dealer’s cards, just as was done for the graphical HighLow card
game in Subsection ??. You can use the source code for that game, High-
LowGUI.java, for some ideas about how to write your Blackjack game. The
structures of the HighLow panel and the Blackjack panel are very similar.
You will certainly want to use the drawCard() method from the HighLow
program.
You can find a description of the game of Blackjack in Exercise 5.5. Add
the following rule to that description: If a player takes five cards without
going over 21, that player wins immediately. This rule is used in some
casinos. For your program, it means that you only have to allow room for
five cards. You should assume that the panel is just wide enough to show
five cards, and that it is tall enough show the user’s hand and the dealer’s
hand.
Note that the design of a GUI Blackjack game is very different from the
design of the text-oriented program that you wrote for Exercise 5.5. The
user should play the game by clicking on “Hit” and “Stand” buttons. There
should be a “New Game” button that can be used to start another game
after one game ends. You have to decide what happens when each of these
buttons is pressed. You don’t have much chance of getting this right unless
you think in terms of the states that the game can be in and how the state
can change.

181
. Graphical User Interface Programming

Your program will need the classes defined in Card.java, Hand.java,


Deck.java, and BlackjackHand.java.
The next exercise has a picture of a blackjack game that you can use a
guide, except that the version for this exercise does not allow betting.

9. In the Blackjack game from Exercise 6.10, the user can click on the “Hit”,
“Stand”, and “NewGame” buttons even when it doesn’t make sense to do so.
It would be better if the buttons were disabled at the appropriate times. The
“New Game” button should be disabled when there is a game in progress.
The “Hit” and “Stand” buttons should be disabled when there is not a game
in progress. The instance variable gameInProgress tells whether or not a
game is in progress, so you just have to make sure that the buttons are prop-
erly enabled and disabled whenever this variable changes value. I strongly
advise writing a method that can be called whenever it is necessary to set
the value of the gameInProgress variable. Then the method can take re-
sponsibility for enabling and disabling the buttons. Recall that if bttn is
a variable of type JButton, then bttn.setEnabled(false) disables the but-
ton and bttn.setEnabled(true) enables the button.
As a second (and more difficult) improvement, make it possible for the
user to place bets on the Blackjack game. When the program starts, give
the user $100. Add a JTextField to the strip of controls along the bottom
of the panel. The user can enter the bet in this JTextField. When the game
begins, check the amount of the bet. You should do this when the game
begins, not when it ends, because several errors can occur: The contents
of the JTextField might not be a legal number, the bet that the user places
might be more money than the user has, or the bet might be <= 0. You
should detect these errors and show an error message instead of starting the
game. The user’s bet should be an integral number of dollars.
It would be a good idea to make the JTextField uneditable while the
game is in progress. If betInput is the JTextField, you can make it editable
and uneditable by the user with the commands betInput.setEditable(true)
and betInput.setEditable(false).
In the paintComponent() method, you should include commands to
display the amount of money that the user has left.
There is one other thing to think about: Ideally, the program should
not start a new game when it is first created. The user should have a chance
to set a bet amount before the game starts. So, in the constructor for the
drawing surface class, you should not call doNewGame(). You might want
to display a message such as “Welcome to Blackjack” before the first game
starts.
Here is a picture of my program:

182
Exercises

183
. Graphical User Interface Programming

Quiz on Chapter 5

1. Programs written for a graphical user interface have to deal with “events.”
Explain what is meant by the term event. Give at least two different exam-
ples of events, and discuss how a program might respond to those events.
2. Explain carefully what the repaint() method does.
3. Java has a standard class called JPanel. Discuss two ways in which JPanels
can be used.
4. Draw the picture that will be produced by the following paintComponent()
method:
public static void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i=10; i \<= 210; i = i + 50)
for (int j = 10; j \<= 210; j = j + 50)
g.drawLine(i,10,j,60);

5. Suppose you would like a panel that displays a green square inside a red
circle, as illustrated. Write a paintComponent() method for the panel class
that will draw the image.

6. Java has a standard class called MouseEvent. What is the purpose of this
class? What does an object of type MouseEvent do?
7. One of the main classes in Swing is the JComponent class. What is meant
by a component? What are some examples?
8. What is the function of a LayoutManager in Java?
9. Consider the illustration of nested panels from the beginning of Section 5.5.
What type of layout manager is being used for each of the three panels in
that picture?

184
Quiz

10. Explain how Timers are used to do animation.

11. What is a JCheckBox and how is it used?

12. How is the preferred size of a component set, and how is it used?

185

You might also like