Object Oriented Programming: David J. Eck Bradley P. Kjell Anban W. Pillay
Object Oriented Programming: David J. Eck Bradley P. Kjell Anban W. Pillay
David J. Eck
Bradley P. Kjell
Anban W. Pillay
Object Oriented Programming in Java
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
i
Contents
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
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
1
. Introduction to Objects
• 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?
3
. Introduction to Objects
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
4
.. Fundamentals of Objects and Classes
State
represented
by variables
Behavour
represented
by methods
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.
5
. Introduction to Objects
message
object A
object B
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:
Classes
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
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.
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; }
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 {
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;
In Java, no variable can ever hold an object. A variable can only hold a
reference to an object.
10
.. Fundamentals of Objects and Classes
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.
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 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();
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
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
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.”
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
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 {
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:
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.
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.
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 PairOfDice() {
// Constructor. Rolls the dice , so that they
initially
19
. Introduction to Objects
20
.. Fundamentals of Objects and Classes
countRolls = 0;
} // end main ()
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.
22
.. Fundamentals of Objects and Classes
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.
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.)
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
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
}
28
.. Javadoc
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
29
. Introduction to Objects
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
@return description-of-return-value
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;
}
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
Toolboxes
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.
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.
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;
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.*;
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.
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;
39
. Introduction to Objects
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 {
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
Static Import
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.*;
42
Chapter
2
Object Oriented
Analysis and Design
† 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;
43
. Object Oriented Analysis and Design
† Software engineering is
44
.. Software Engineering
• 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.
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.
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.
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.
47
. Object Oriented Analysis and Design
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.
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
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
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
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
• 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
• Sequence Diagrams show objects and a sequence of method calls they make
to other objects.
• 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
• Deployment Diagrams show the instances of the components and their re-
lationships.
52
.. Documenting Designs: The Unified Modelling Language
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:
53
. Object Oriented Analysis and Design
• 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:
54
.. Documenting Designs: The Unified Modelling Language
55
. Object Oriented Analysis and Design
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.
56
.. Documenting Designs: The Unified Modelling Language
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 .
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.
58
.. Documenting Designs: The Unified Modelling Language
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
60
.. Documenting Designs: The Unified Modelling Language
61
. Object Oriented Analysis and Design
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.
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()
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.
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() {
64
.. A Case Study: Card Games
/** 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) {
/** 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
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;
67
. Object Oriented Analysis and Design
/** 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;
68
.. A Case Study: Card Games
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();
}
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 {
70
.. Example: A Simple Card 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);
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() {
correctGuesses = 0;
currentCard = deck.dealCard();
System.out.println("The first card is the " + currentCard);
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');
nextCard = deck.dealCard();
System.out.println("The next card is " + nextCard);
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.
}
}
currentCard = nextCard;
System.out.println();
System.out.println("The card is " + currentCard);
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
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
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.
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
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.
77
. Inheritance
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
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;
}
79
. Inheritance
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.
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
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;
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:
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.
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.
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
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.
85
. Inheritance
Overloading Example
public void changeSize(int size, String name, float pattern) {
}
To reiterate the ideas discussed thus far, we give an example of another subclass
of Video.
86
.. Another Example subclass
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 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.
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
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:
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
89
. Inheritance
Type Checking
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;
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);
}
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.
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");
}
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
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
*
*/
protectedMethod();
protectedVariable =7;
}
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.*;
97
Chapter
4
Abstract Classes,
Interfaces and Inner
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 redraw() {
// method for drawing the shape
? ? ? // what commands should go here?
}
99
. Abstract Classes, Interfaces and Inner Classes
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
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
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 {
102
.. Abstract Classes
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:
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.
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);
}
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 )
{ . . . }
}
104
.. Abstract Classes
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.
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.
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.
105
. Abstract Classes, Interfaces and Inner Classes
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.
106
.. Interfaces
interface InterfaceName {
constant definitions
A class always extends just one parent but may implement several interfaces.
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.
• Constants from the interface can be used as if they had been defined in the
class.
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 {
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 {
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 {
Example Problem
• description
• price
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,
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
110
.. Interfaces
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;
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.
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:
You might wish to review the diagram that shows the relationships between
the classes.
public class Book extends Goods implements Taxable{
private String author;
112
.. Interfaces
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{
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
{
113
. Abstract Classes, Interfaces and Inner Classes
{
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.
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.
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()
);
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();
}
}
117
. Abstract Classes, Interfaces and Inner Classes
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
119
. Abstract Classes, Interfaces and Inner 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
} // 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
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.
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.
123
. Abstract Classes, Interfaces and Inner Classes
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")
(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.
125
. Graphical User Interface Programming
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.
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
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 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
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
OK
actionPerformed()
Visible button,
represented by Event-handling object
a JButton object of type ActionListener
responds to the event
131
. Graphical User Interface Programming
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.
}
133
. Graphical User Interface Programming
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
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
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
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.
.
}
139
. Graphical User Interface Programming
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
/∗ ∗
∗/
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 .
/∗ ∗
∗/
141
. Graphical User Interface Programming
∗/
142
.. Mouse Events
int x ,y;
x = −50 + (int)( Math . random ()∗( width +40));
y = (int)( Math . random ()∗( height +20));
g . drawString ( message ,x , y );
} // end for
} // end p a i n t C o m p o n e n t ()
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 ;
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.
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
/∗ ∗
∗ repainted .
∗/
clicked .
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 {
148
.. Mouse Events
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
/**
* 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 {
//
----------------------------------------------------------------------
/**
* 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;
}
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
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.
153
. Graphical User Interface Programming
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;
154
.. Mouse Events
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
156
.. Mouse Events
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* 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
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
160
.. Mouse Events
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.
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
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:
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 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 ??.
169
. Graphical User Interface Programming
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
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
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
#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.
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
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) {
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;
}
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
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
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
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
12. How is the preferred size of a component set, and how is it used?
185