Java Classes and Abstract Data Types
Java Classes and Abstract Data Types
35
2.1
2.2
USING A CLASS
2.3
PACKAGES
2.4
& + $$33 7 ( 5
CHAPTER SUMMARY
SOLUTIONS TO SELF-TEST EXERCISES
PROGRAMMING PROJECTS
bject-oriented programming (OOP) is an approach to programming where data occurs in tidy packages called objects. Manipulation of an
object happens with functions called methods, which are part and parcel of their
objects. The Java mechanism to create objects and methods is called a class. In
fact, the keyword class at the start of each Java application program indicates
that the program is itself a class with its own methods to carry out tasks.
This chapter moves you beyond small Java application programs. Your goal
is to be able to write general purpose classes that can be used by many different
programs. Each general purpose class will capture a certain functionality, and an
application programmer can look through the available classes to select those
that are useful for the job at hand.
35
ADTs
emphasize the
specification
rather than the
implementation
2.1
A class is a new kind of data type. Each of your classes includes various data,
such as integers, characters, and so on. In addition, a class has the ability to
include two other items: constructors and methods. Constructors are designed to
provide initial values to the classs data; methods are designed to manipulate the
data. Taken together, the data, constructors, and methods of a class are called the
class members.
But this abstract discussion does not really tell you what a class is. We need
some examples. As you read the first example, concentrate on learning the techniques for implementing a class. Also notice how you use a class written by
another programmer, without knowing details of the classs implementation.
PROGRAMMING EXAMPLE: The Throttle Class
FAST
SLOW
OFF
Our first example of a class is a new data type to store and manipulate
the status of a mechanical throttle. An object of this new class holds
information about a throttle, as shown in the picture. The throttle is a
lever that can be moved to control fuel flow. The throttle we have in
mind has a single shutoff point (where there is no fuel flow) and a
sequence of several on positions where the fuel is flowing at
37
progressively higher rates. At the topmost position, the fuel flow is fully on. At
intermediate positions, the fuel flow is proportional to the location of the lever.
For example, with six possible positions, and the lever in the fourth position, the
4
fuel flows at --6- of its maximum rate.
A constructor is designed to provide initial values to a classs data. The throttle constructor permits a program to create a new throttle with a specified number
of on positions above the shutoff position. For instance, a throttle for a lawn
mower could specify six positions, whereas a throttle for a Martian lander could
specify 1000 positions. The throttles lever is initially placed in the shutoff
position.
Once a throttle has been initialized, there two methods to shift the throttles
lever: One of the methods shifts the lever by a given amount, and the other
method returns the lever to the shutoff position. We also have two methods to
examine the status of a throttle. The first of these methods returns the amount of
fuel currently flowing, expressed as a proportion of the maximum flow. For
example, this method will return approximately 0.667 when a six-position throttle is in its fourth position. The other method returns a true-or-false value, telling
whether the throttle is currently on (that is, whether the lever is above the shutoff
position). Thus, the throttle has one constructor and four methods listed here:
A constructor to create a new throttle with one shutoff position and a
specified number of on positions (the lever starts in the shutoff position)
A method that returns the fuel flow, expressed as a proportion of the maximum flow
one throttle
constructor and
four throttle
methods
declaring the
Throttle class
three varieties of
class members
appear in the
class definition
This class definition defines a new data type called Throttle. The definition
starts with the class head, which consists of the Java keywords public class,
followed by the name of the new class. The keyword public is necessary before
the class because we want to allow all other programmers (the public) to use
the new class. The name of the class may be any legal identifier. We chose the
name Throttle. We always use a capital letter for the first character of names
of new classesthis isnt required by Java, but its a common programming
style, making it easy to identify class names.
The rest of the class definition, between the two brackets, lists all the components of the class. These components are called members of the class and they
come in three varieties: instance variables, constructors, and methods.
Instance Variables
The first kind of member is a variable declaration. These variables are called
instance variables (or sometimes member variables). The Throttle has two
instance variables:
private int top;
// The topmost position of the lever
private int position; // The current position of the lever
Each instance variable stores some piece of information about the status of an
object. For example, consider a throttle with six possible positions and the lever
in the fourth position. This throttle would have top=6 and position=4.
The keyword private occurs in front of each of our instance variables. This
keyword means that programmers who use the new class have no way to read or
assign values directly to the private instance variables. It is possible to have public instance variables that can be accessed directly, but public instance variables
tend to reveal too much information about how a class is implemented, violating
the principle of information hiding. Therefore, our examples will use private
instance variables. All access to private instance variables is carried out through
the constructors and methods that are provided with the class.
Constructors
The second kind of member is a constructor. A constructor is a method that is
responsible for initializing the instance variables. For example, our constructor
creates a throttle with a specified number of on positions above the shutoff position. This constructor sets the instance variable top to a specified number, and
sets position to zero (so that the throttle is initially shut off).
For the most part, implementing a constructor is no different than your past
work (such as implementing a method for a Java application). The primary difference is that a constructor has access to the classs instance variables, and is
responsible for initializing these variables. Thus, a throttle constructor must provide initial values to top and position. Before you implement the throttle constructor, you must know the several rules that make constructors special:
Before any constructor begins its work, all instance variables are assigned
Java default values. For example, the Java default value for any number
variable is zero.
If an instance variable has an initialization value with its declaration, the
initialization value replaces the default value. For example, suppose we
have this instance variable:
int jackie = 42;
The instance variable jackie is first given its default value of zero; then
the zero is replaced by the initialization value of 42.
The name of a constructor must be the same as the name of the class. In our
example, the name of the constructor is Throttle. This seems strange:
Normally we avoid using the same name for two different things. But it is
a requirement of Java that the constructor use the same name as the class.
A constructor is not really a method, and therefore it does not have any
return value. Because of this, you must not write void (or any other return
type) at the front of the constructors head. The compiler knows that every
constructor has no return value, but a compiler error occurs if you actually
write void at the front of the constructors head.
With these rules, we can write the throttles constructor as shown here (with its
specification following the format from Section 1.1):
number of on positions.
Parameters:
size the number of on positions for this new Throttle
Precondition:
size > 0.
Postcondition:
This Throttle has been initialized with the specified number of on
positions above the shutoff position, and it is currently shut off.
Throws: IllegalArgumentException
Indicates that size is not positive.
public Throttle(int size)
{
if (size <= 0)
throw new IllegalArgumentException("Size <= 0: " + size);
top = size;
// No assignment needed for position -- it gets the default value of zero.
}
This constructor sets top according to the parameter, size. It does not explicitly
set position, but the comment in the implementation indicates that we did not
39
a class may
have many
different
constructors
just forget about positionthe default value of zero is its correct initial value.
The implementation is preceded by the keyword public to make it available to
all programmers.
The throttle has just one constructor, just one way of setting the initial values
of the instance variables. Some classes may have many different constructors
that set initial values in different ways. If there are several constructors, then each
constructor must have a distinct sequence of parameters to distinguish it from the
other constructors.
No-Arguments Constructors
Some classes have a constructor with no parameters, called a no-arguments
constructor. In effect, a no-arguments constructor does not need any extra
information to set the initial values of the instance variables.
If you write a class with no constructors at all, then Java automatically provides a no-arguments constructor that initializes each instance variable to its
initialization value (if there is one) or to its default value (if there is no specified
initialization value). There is one situation where Java does not provide an automatic no-arguments constructor, and youll see this situation when you write
subclasses in Chapter 13.
Methods
The third kind of class member is a method. A method does computations that
access the classs instance variables. Classes tend to have two kinds of methods:
1. Accessor methods. An accessor method gives information about an object
without altering the object. In the case of the throttle, an accessor method can
return information about the status of a throttle, but it must not change the position of the lever.
2. Modification methods. A modification method may change the status of
an object. For a throttle, a modification method may shift the lever up or down.
Each class method is designed for a specific manipulation of an objectin
our case, the manipulation of a throttle. To carry out the manipulations, each of
the throttle methods has access to the throttles instance variables, top and
position. The methods can examine top and position to determine the current status of the throttle, or top and position can be changed in order to alter
the status of the throttle. Lets look at the details of the implementations of the
throttle methods, beginning with the accessor methods.
Accessor Methods
Accessor methods provide information about an object without changing the
object. Accessor methods are often short, just returning the value of an instance
getFlow
public double getFlow( )
isOn
public boolean isOn( )
Check whether this Throttle
is on.
Returns:
If this Throttles flow is above zero, then the return value is true;
otherwise the return value is false.
public boolean isOn( )
{
return (position > 0);
}
accessor
methods often
have no
parameters
PITFALL
41
TIP
Modification Methods
There are two more throttle methods. These two are modification methods,
which means that they are capable of changing the values of the instance variables. Here is the first modification method:
shutOff
public void shutOff( )
Turn off this Throttle.
Postcondition:
This Throttles flow has been shut off.
public void shutOff( )
{
position = 0;
}
modification
methods are
usually void
Modification methods are usually void, meaning that there is no return value. In
the specification of a modification method, the methods work is fully described
in the postcondition.
The throttles shutOff method has no parametersit doesnt need parameters because it just moves the throttles position down to zero, shutting off the
flow. However, most modification methods do have parameters, such as a throttle method to shift the throttles lever by a specified amount. This shift method
has one integer parameter called amount. If amount is positive, then the throttles
lever is moved up by that amount (but never beyond the topmost position). A
negative amount causes the lever to move down (but never below zero). The
specification and implementation appear at the top of the next page.
shift
public void shift(int amount)
This might be the first time youve seen the += operator. Its effect is to take the
value on the right side (such as amount) and add it to whats already in the variable on the left (such as position). This sum is then stored back in the variable
on the left side of +=.
The shift method requires care to ensure that the position does not go above
the topmost position nor below zero. For example, the first test in the method
checks whether (amount > top - position). If so, then adding amount to
position would push the position over top. In this case, we simply set
position to top.
It is tempting to write the test (amount > top - position) in a slightly different way, like this:
if (position + amount > top)
// Adding amount would put the position above the top.
position = top;
This seems okay at first glance, but there is a potential problem: What happens
if both position and amount are large integers such as 2,000,000,000? The
subexpression position + amount should be 4,000,000,000, but Java tries to
temporarily store the subexpression as a Java integer, which is limited to the
range 2,147,483,648 to 2,147,483,647. The result is an arithmetic overflow,
which is defined as trying to compute or store a number that is beyond the legal
43
range of the data type. When an arithmetic overflow occurs, the program might
stop with an error message or it might continue computing with wrong data.
We avoided the arithmetic overflow by rearranging the first test to avoid the
troublesome subexpression. The test we use is:
if (amount > top - position)
// Adding amount would put the position above the top.
position = top;
This test uses the subexpression top - position. Since top is never negative,
and position is in the range [0...top], the subexpression top - position is
always a valid integer in the range [0...top].
What about the second test in the method? In the second test, we use the subexpression position + amount, but at this point, position + amount can no
longer cause an arithmetic overflow. Do you see why? If position + amount is
bigger than top, then the first test would have been true and the second test is
never reached. Therefore, by the time we reach the second test, the subexpression
position + amount is guaranteed to be in the range [amount...top], and arithmetic overflow cannot occur.
PITFALL
FIGURE 2.1
We have completed the Throttle class implementation and can now put the
complete definition in a file called [Link], as shown in Figure 2.1. The
name of the file must be [Link] since the class is Throttle.
Class Throttle
public class Throttle
A Throttle object simulates a throttle that is controlling fuel flow.
(continued)
45
Specification
number of on positions.
Parameters:
size the number of on positions for this new Throttle
Precondition:
size > 0.
Postcondition:
This Throttle has been initialized with the specified number of on positions above the
shutoff position, and it is currently shut off.
Throws: IllegalArgumentException
Indicates that size is not positive.
getFlow
public double getFlow( )
Get the current flow of this Throttle.
Returns:
the current flow rate (always in the range [0.0 ... 1.0] ) as a proportion of the maximum flow
isOn
public boolean isOn( )
Check whether this Throttle
is on.
Returns:
If this Throttles flow is above zero, then the return value is true; otherwise the return value
is false.
shift
public void shift(int amount)
Move this Throttles position up or
down.
Parameters:
amount the amount to move the position up or down (a positive amount moves the position
up, a negative amount moves it down)
Postcondition:
This Throttles position has been moved by the specified amount. If the result is more than
the topmost position, then the position stays at the topmost position. If the result is less than
the zero position, then the position stays at the zero position.
shutOff
public void shutOff( )
Turn off this Throttle.
Postcondition:
This Throttle has been shut off.
(continued)
Implementation
// File: [Link]
public class Throttle
{
private int top;
// The topmost position of the throttle
private int position; // The current position of the throttle
public Throttle(int size)
{
if (size <= 0)
throw new IllegalArgumentException("Size <= 0: " + size);
top = size;
// No assignment needed for position -- it gets the default value of zero.
}
public double getFlow( )
{
return (double) position / (double) top;
}
public boolean isOn( )
{
return (getFlow( ) > 0);
}
public void shift(int amount)
{
if (amount > top - position)
// Adding amount would put the position above the top.
position = top;
else if (position + amount < 0)
// Adding amount would put the position below zero.
position = 0;
else
// Adding amount puts position in the range [0...top].
position += amount;
}
public void shutOff( )
{
position = 0;
}
}
Self-Test Exercises
1. Name and describe the three kinds of class members we have used. In
this section, which kinds of members were public and which were
private?
2. Write a new throttle constructor with no arguments. The constructor sets
the top position to 1 and sets the current position off.
3. Write another throttle constructor with two arguments: the total number
of positions for the throttle, and its initial position.
4. Add a new throttle method that will return true if the current flow is
more than half. The body of your implementation should activate getFlow.
TIP
47
5. Design and implement a class called Clock. A Clock object holds one
instance of a time value such as 9:48 P.M. Have at least these public
methods:
A no-arguments constructor that initializes the time to midnightsee
page 40 for the discussion of a no-arguments constructor
A method to explicitly assign a given timeyou will have to give
some thought to appropriate arguments for this method
Methods to retrieve information: the current hour, the current minute,
and a boolean method to determine whether the time is at or before
noon
A method to advance the time forward by a given number of minutes
(which could be negative to move the clock backward or positive to
move the clock forward)
2.2
programs can
create new
objects of a
class
USING A CLASS
How do you use a new class such as Throttle? Within any program, you may
create new throttles, and refer to these throttles by names that you define. We
can illustrate the general syntax for creating and using these objects by an
example.
Creating and Using Objects
Suppose a program needs a new throttle with 100 positions above the shutoff.
Within the program, we want to refer to the throttle by the name control. The
Java syntax has these parts:
Throttle control = new Throttle(100);
Using a Class
Once the throttle is created, we can refer to the throttle by the name that we
selected: control. For example, suppose we want to shift the lever up to its third
notch. We do this by calling the shift method, as shown here:
[Link](3);
Notice how the return value of [Link] is used directly in the output
statement. As with any other method, the return value of an accessor method can
be used as part of an output statement or other expression. The output from this
code is:
My small throttle is now at position 3 out of 8.
The flow is now: 0.375
how to use a
method
49
In the example above, tiny has its own instance variables (top will be 4 and
position will be 2); huge also has its own instance variables (top will be 10000
and position will be 2500). When we activate a method such as [Link],
the method uses the instance variables from tiny; when we activate
[Link], the method uses the instance variables from huge.
The variables in our examplescontrol, small, tiny, hugeare called
reference variables because they are used to refer to objects (in our case,
throttles). There are several differences between a reference variable (used by
Java for all classes) and an ordinary variable (used by Java for the primitive data
types of int, char, and so on). Lets look at these differences, beginning with a
special value called null that is used only with reference variables.
Null References
The creation of a new object can be separated from the declaration of a variable.
For example, the following two statements can occur far apart in a program:
Throttle control;
...
control = new Throttle(100);
Once both statements finish, control refers to a newly created throttle with 100
positions. But what is the status of control between the statements? At this
point, control does not yet refer to any throttle, because we havent yet created a
throttle. In this situation, we can assign a special value to control, indicating that
control does not yet refer to anything. The value is called the null reference, written with the keyword null in Java. So we could change the above example to this:
Throttle control = null;
...
control = new Throttle(100);
Using a Class
Null Reference
Sometimes a reference variable does not refer to anything.
This is a null reference, and the value of the variable is
called null.
Sometimes a program finishes using an object. In this case, the program may
explicitly set a reference variable to null, as shown here:
Throttle control = new Throttle(100);
// Various statements that use the Throttle appear next...
...
// Now we are done with the control Throttle, so we can set
// the reference to null.
control = null;
Once a reference variable is no longer needed, its a good idea to set it to null,
allowing Java to economize on certain resources (such as the memory used by a
throttle).
PITFALL
51
refer to the same object that t1 is already refering to. In other words, we have
two reference variables (t1 and t2), but we created only one throttle (with one
new statement). This one throttle has 100 positions, and is currently in the 25th
position. After the assignment statement, both t1 and t2 refer to this one throttle.
As an example, lets start with the two declarations:
Throttle t1;
Throttle t2;
We now have two variables, t1 and t2. If these variables are declared in a
method, then they dont yet have an initial value (not even null). We can draw
this situation with a question mark for each value, as shown here:
Throttle t1
Throttle t2
These statements create a new throttle for t1 to refer to, and shift the throttles
position to 25. We will draw a separate box for the throttle and indicate its
instance variables (top at 100 and position at 25). To show that t1 refers to
this throttle, we draw an arrow from the t1 box to the throttle, like this:
Throttle t1
Throttle t2
top 100
A Throttle position 25
object
After the assignment, t2 will refer to the same object that t1 refers to, as shown
here:
Throttle t1
Throttle t2
top 100
A Throttle position 25
object
Using a Class
There are now two references to the same throttle, which can cause some
surprising results. For example, suppose we shift t2 down five notches and then
print the flow of t1, like this:
[Link](-5);
[Link](Flow of t1 is: + [Link]( ));
What flow rate is printed? The t1 throttle was set to position 25 out of 100, and
we never directly altered its position. But [Link](-5) moves the throttles
position down to 20. Since t1 refers to this same throttle, [Link] now
returns 20/100, and the output statement prints Flow of t1 is: 0.2. Heres the
entire code that we executed and the final situation drawn as a picture:
Throttle t1;
Throttle t2;
t1 = new Throttle(100);
[Link](25);
t2 = t1;
[Link](-5);
Throttle t2
Throttle t1
top 100
A Throttle position 20
object
53
Throttle t2
top 100
A Throttle position 25
object
top 100
A Throttle position 25
object
Changes that are now made to one throttle will not effect the other, because
there are two completely separate throttles.
Clones
A programmer sometimes needs to make an exact copy of an existing object.
The copy must be just like the existing object, but separate. Subsequent changes
to the copy should not alter the original, nor should subsequent changes to the
original alter the copy. A separate copy such as this is called a clone.
An assignment operation t2 = t1 does not create a clone, and in fact the
Throttle class does not permit the easy creation of clones. But many other
classes have a special method called clone for just this purpose. Writing a useful
clone method has some requirements that may not be evident just now, so we
will postpone a complete discussion until Section 2.4.
Testing for Equality
A test for equality (t1 == t2) can be carried out with reference variables. The
equality test (t1 == t2) is true if both t1 and t2 are null, or if they both refer
to the exact same object (not two different objects that happen to have the same
values for their instance variables). An inequality test (t1 != t2) can also be
carried out. The result of an inequality test is always the opposite of an equality
test. Lets look at two examples.
Using a Class
The first example creates just one throttle; t1 and t2 both refer to this throttle
as shown in the following picture:
Throttle t1;
Throttle t2;
t1 = new Throttle(100);
[Link](25);
t2 = t1;
Throttle t2
Throttle t1
top 100
A Throttle position 25
object
At this point in the computation, (t1 == t2) is true. Both reference variables
refer to the same object.
On the other hand, consider this code, which creates two separate throttles:
Throttle t1;
Throttle t2;
t1 = new Throttle(100);
[Link](25);
t2 = new Throttle(100);
[Link](25);
Throttle t2
Throttle t1
top 100
A Throttle position 25
object
top 100
A Throttle position 25
object
After this computation, (t1 == t2) is false. The two throttles have the same
value (with top at 100 and position at 25), but the equality test returns false
because they are two separate throttles.
Test for Equality with Reference Variables
For reference variables t1 and t2, the test (t1 == t2) is
true if both references are null, or if t1 and t2 refer to the
exact same object (not two different objects that happen to
have the same values for their instance variables).
55
10. Consider the code from the previous question. At the end of the computation, is (t1 == t2) true or false?
11. Write some code that will make t1 and t2 refer to two different throttles
with 100 positions each. Both throttles are shifted up to position 42. At
the end of your code, is (t1 == t2) true or false?
2.3
PACKAGES
You now know enough to write a Java application program that uses a throttle.
The Throttle class would be in one file ([Link] from Figure 2.1 on
page 46) and the program that uses the Throttle class would be in a separate
file. However, theres one more level of organization that will make it easier for
other programmers to use your classes. The organization, called a Java package,
is a group of related classes put together in a way that makes it easy for
programs to use the classes.
Packages
57
Declaring a Package
The first step in declaring a package of related classes is to decide on a name for
the package. For example, perhaps we are declaring a bunch of Java classes to
simulate various real-world devices such as a throttle. A good short name for the
package is the simulations package. But theres a problem with good short
names: Other programmers might decide to use the same good short name for
their packages, resulting in the same name for two different packages.
The solution is to include your Internet domain name as part of the package
name. For example, at the University of Colorado the Internet domain name is
[Link] (my e-mail address is main@[Link]). Therefore, instead
of using the package name simulations, I will use the longer package name
[Link] (package names may include a dot as part of the
name). Many programmers follow this convention, using the Internet domain
name in reverse. The only likely conflicts are with other programmers at your
own Internet domain, and those conflicts can be prevented by internal
cooperation.
Once you have decided on a package name, a package declaration must be
made at the top of each source file of the package. The package declaration consists of the keyword package followed by the full package name and a semicolon. The declaration appears at the start of each source file, before any class
declarations. For example, the start of [Link] is changed to include the
package declaration shown here:
package [Link];
Implementation
// File: [Link] from the package [Link]
// Documentation is in Figure 2.1 on page 44 or from the Throttle link in
//
[Link]
package [Link];
the package
declaration
public class Throttle
{
private int top;
// The topmost position of the throttle
private int position; // The current position of the throttle
public Throttle(int size)
{
if (size <= 0)
throw new IllegalArgumentException("Size <= 0: " + size);
top = size;
// No assignment needed for position -- it gets the default value of zero.
}
public double getFlow( )
{
return (double) position / (double) top;
}
public boolean isOn( )
{
return (getFlow( ) > 0);
}
public void shift(int amount)
{
if (amount > top - position)
// Adding amount would put the position above the top.
position = top;
else if (position + amount < 0)
// Adding amount would put the position below zero.
position = 0;
else
// Adding amount puts position in the range [0...top].
position += amount;
}
(continued)
Packages
59
If only a few classes from a package are needed, then each class can be imported
separately. For example, this statement imports only the Throttle class from
the [Link] package:
import [Link];
After this import statement, the Throttle class can be used . For example, a
program can declare a variable:
Throttle control;
A sample program using our throttle appears in Figure 2.3. The program
creates a new throttle, shifts the throttle fully on, and then steps the throttle back
down to the shut off position.
The JCL Packages
The Java language comes with many useful packages called the Java Class
Libraries (JCL). Any programmer can use various parts of the JCL by including an appropriate import statement. In fact, one of the packages, [Link], is
so useful that it is automatically imported into every Java program. Some parts
of the JCL are described in Appendix D.
a program can
use an entire
package or just
parts of a
package
the import
statement
class ThrottleDemonstration
{
public static void main(String[ ] args)
{
final int SIZE = 8; // The size of the demonstration Throttle
Throttle small = new Throttle(SIZE);
[Link]("I am now shifting a Throttle fully on, and then I");
[Link]("will shift it back to the shut off position.");
[Link](SIZE);
while ([Link]( ))
{
[Link]("The flow is now " + [Link]( ));
[Link](-1);
}
[Link]("The flow is now off");
}
}
2.4
61
1
p
0
-1
-2
-2
-1
1
p
0
q
-1
-2
-2
-1
0
-1
-2
-2
-1
The Location class is small, yet it forms the basis for an actual data type that
is used in drawing programs and other graphics applications. All the methods
and the constructor are listed in the specification of Figure 2.5. The figure also
shows one way to implement the class. After youve looked through the figure,
well discuss that implementation.
FIGURE 2.5
63
Class Location
public class Location from the package [Link]
A Location object keeps track of a location on a two-dimensional plane.
Specification
Parameters:
xInitial the initial x coordinate of this Location
yInitial the initial y coordinate of this Location
Postcondition:
This Location has been initialized at the given coordinates.
clone
public Object clone( )
Generate a copy of this Location.
Returns:
The return value is a copy of this Location. Subsequent changes to the copy will not affect
the original, nor vice versa. Note that the return value must be typecast to a Location before
it can be used.
distance
public static double distance(Location p1, Location p2)
Compute the distance between two Locations.
Parameters:
p1 the first Location
p2 the second Location
Returns:
the distance between p1 and p2
Note:
The answer is Double.POSITIVE_INFINITY if the distance calculation overflows. The answer
is [Link] if either Location is null.
(continued)
equals
public boolean equals(Object obj)
Compare this Location to another object
for equality.
Parameters:
obj an object with which this Location is compared
Returns:
A return value of true indicates that obj refers to a Location object with the same value as
this Location. Otherwise the return value is false.
Note:
If obj is null or it is not a Location object, then the answer is false.
double getY( )
midpoint
public static Location midpoint(Location p1, Location p2)
Generates and returns a Location halfway between two others.
Parameters:
p1 the first Location
p2 the second Location
Returns:
a Location that is halfway between p1 and p2
Note:
The answer is null if either p1 or p2 is null.
rotate90
public void rotate90( )
Rotate the Location 90 in a
clockwise direction.
Postcondition:
This Location has been rotated clockwise 90 around the origin.
shift
public void shift(double xAmount, double yAmount)
Move this Location by given amounts along the x and y axes.
Postcondition:
This Location has been moved by the given amounts along the two axes.
Note:
The shift may cause a coordinate to go above Double.MAX_VALUE or below
Double.MAX_VALUE. In these cases, subsequent calls of getX or getY will return
Double.POSITIVE_INFINITY or Double.NEGATIVE_INFINITY.
(continued)
65
toString
public String toString( )
Implementation
// File: [Link] from the package [Link]
// Documentation is available on pages 6364 or from the Location link in
//
[Link]
package [Link];
public class Location implements Cloneable
{
private double x; // The x coordinate of the Location
private double y; // The y coordinate of the Location
the meaning of
implements Cloneable
and the clone method are
discussed on page 76
(continued)
on page 68
// Check whether one of the Locations is null.
if ((p1 == null) || (p2 == null))
return [Link];
// Calculate differences in x and y coordinates.
a = p1.x - p2.x;
b = p1.y - p2.y;
(continued)
67
Static Methods
The implementation of the Location class has several features that may be new
to you. Some of the features are in a method called distance, with this
specification:
distance
public static double distance(Location p1, Location p2)
The distance
between p and s
can be computed
with the
Pythagorean
Theorem.
2
p
1
0
dis
tan
ce
-1
s
-2
-2
-1
x -1.0
A Location y 0.8
object
x 1.7
A Location y -1.2
object
The names used within the method (p1 and p2) are usually called parameters to
distinguish them from the values that are passsed in (p and s). On the other
hand, the values that are passed in (p and s) are called the arguments. Anyway,
the first step of any method activation is to use the arguments to provide initial
values for the parameters. Heres the important fact you need to know about
objects:
parameters
versus
arguments
Location s
Location p
Location p1
x 1.7
x -1.0
A Location
object
y 0.8
A Location
object
y -1.2
Location p2
69
be careful about
changing the
value of a
parameter
Within the body of the distance method we can access p1 and p2. For example, we can access p1.x to obtain the x coordinate of the first parameter. This
kind of access is okay in a static method. The only forbidden expression is a
direct x or y (without a qualifier such as p1).
Some care is needed in accessing a parameter that is an object. For instance,
any change to p1.x will affect the actual argument p.x. We dont want the
distance method to make changes to its arguments; it should just compute the
distance between the two locations and return the answer. This computation
occurs in the implementation of distance on page 66.
The implementation also handles a couple of special cases. One special case
is when an argument is null. In this case, the corresponding parameter will be
initialized as null, and the distance method executes this code:
// Check whether one of the Locations is null.
if ((p1 == null) || (p2 == null))
return [Link];
the not-anumber
constant
the infinity
constant
If either parameter is null, then the method returns a Java constant named
[Link]. This is a constant that a program uses to indicate that a double
value is not a number.
Another special case for the distance method is the possibility of a numerical
overflow. The numbers obtained during a computation may go above the largest
double number or below the smallest double number. These numbers are pretty
large, but the possibility of overflow still exists. When an arithmetic expression
with double numbers goes beyond the legal range, Java assigns a special constant
to the answer. The constant is named Double.POSITIVE_INFINITY if it is too
large (above about 1.7308), and it is named Double.NEGATIVE_INFINITY if it is
too small (below about 1.7308). Of course, these constants are not really infinity. They are merely indications to the programmer that a computation has overflowed. In the distance method, we indicate the possibility of overflow with the
following comment:
Note:
The answer is Double.POSITIVE_INFINITY if the distance calculation
overflows. The answer is [Link] if either Location is null.
midpoint
public static Location midpoint(Location p1, Location p2)
The method creates a new location using the local variable answer, and then
returns this location. Often the return value is stored in a local variable such as
answer, but not always. For example, we could have eliminated answer by
combining the last two statements in our implementation to a single statement:
return new Location(xMid, yMid);
In this example, the answer from the midpoint method is stored in a variable
called medium. After the three statements, we have three locations, drawn at the
top of the next page.
71
x 0
Location low
A Location y 0
object
x 1000
Location high
A Location y 5280
object
x 500
Location medium
TIP
A Location y 2640
object
An accessor method with this name has a special meaning in Java. Before we
discuss that meaning, you need to know a bit about the parameter type
Object. In Java, Object is a kind of super data type that encompasses all
data except the eight primitive types. So a primitive variable (byte, short, int,
long, char, float, double, or boolean) is not an Object, but everything else
is. A String is an Object, a Location is an Object, even an array is an
Object.
A Location y 2
object
x 10
A Location y 0
object
In this example, p and s refer to two separate objects with different values (their
y coordinates are different), so both [Link](s) and [Link](p) are false.
Heres a slightly different example:
Location p = new Location(10, 2); // Declare p at coordinates (10,2)
Location s = new Location(10, 0); // Declare s at coordinates (10,0)
[Link](0, 2);
// Move s to (10,2)
We have the same two declarations, but afterward we shift the y coordinate of s
so that the two separate locations have identical values, like this:
Location s
Location p
x 10
A Location y 2
object
x 10
A Location y 2
object
73
The argument to the equals method can be any object, not just a location. For
example, we can try to compare a location with a string, like this:
Location p = new Location(10, 2);
[Link]([Link]("10, 2"); // Prints false.
This example prints false; a Location object is not equal to the string "10, 2"
even if they are similar. You can also test to see whether a location is equal to null,
like this:
Location p = new Location(10, 2);
[Link]([Link](null)); // Prints false.
Now you know how to use an equals method. How do you write an equals
method so that it returns true when its argument has the same value as the object
that activates the method? A typical implementation follows an outline that is
used for the equals method of the Location class, as shown here:
public boolean equals(Object obj)
{
if ( obj is actually a Location )
{
Figure out whether the location that obj refers to has the same
value as the location that activated this method. Return true if
they are the same, otherwise return false.
}
else
return false;
}
the instanceof
operator
object. We need to determine whether the x and y coordinates of obj are the same
as the location that activated the method. Unfortunately, we cant just look at
obj.x and obj.y because the compiler thinks of obj as a bare object with no x
and y instance variables. The solution is an expression (Location) obj . This
expression is called a typecast, as if we were pouring obj into a casting mold that
creates a Location object. The expression can be used to initialize a Location
reference variable, like this:
Location candidate = (Location) obj;
The typecast, on the right side of the declaration, consists of the new data type
(Location) in parentheses, followed by the reference variable that is being
cast. After this declaration, candidate is a reference variable that refers to
the same object that obj refers to. However, the compiler does know that
candidate refers to a Location object, so we can look at candidate.x and
candidate.y to see if they are the same as the x and y coordinates of the object
that activated the equals method. The complete implementation looks like this:
public boolean equals(Object obj)
{
if (obj instanceof Location)
{
Location candidate = (Location) obj;
return (candidate.x == x) && (candidate.y == y);
}
else
return false;
}
PITFALL
75
Typecasts
Within the implementation of the equals method, we need to treat obj as a Location rather
than a mere Object. The solution has two parts: (1) Check that obj does indeed refer to a valid
Location, and (2) Declare a new variable of type Location, and initialize this new variable to
refer to the same object that obj refers to, like this:
The parameter, obj, is an Object
public boolean equals(Object obj)
{
Use the instanceof operator to check that
if (obj instanceof Location)
obj is a valid Location
{
Location candidate = (Location) obj;
...
After this declaration, candidate refers to
the original, nor will subsequent changes to the original change the copy.
Heres an example showing how the clone method is used for the Location
class:
Location p = new Location(10, 2);
// Declare p at (10,2)
Location s = (Location) [Link]( ); // Initialize as a copy of p
The expression [Link]( ) activates the clone method for p. The method creates and returns an exact copy of p, which we use to initialize the new location
s. After these two declarations, we have two separate locations, as shown in this
picture:
Location s
Location p
x 10
A Location
y 2
object
x 10
A Location
y 2
object
As you can see, s and p have the same values for their instance variables, but the
two objects are separate. Changes to p will not affect s, nor will changes to s
affect p.
PITFALL
77
After these two declarations, we have just one location, and both variables refer
to this location:
Location s
Location p
x 10
A Location
y 2
object
implementing a
clone method
The modification informs the Java compiler that you plan to implement certain
features that are specified elsewhere in a format called an interface. The full
meaning of interfaces will be discussed in Chapter 5. At the moment, it is
enough to know that implements Cloneable is necessary when you implement
a clone method.
By the way, Cloneable is a misspelling of Clonable. Some future version
of Java may correct the spelling, but for now its nice to know that spell checkers
havent completely taken over the world.
2. Use [Link] to make a copy. The implementation of a clone method
should begin by making a copy of the object that activated the method. The best
way to make the copy is to follow this pattern from the Location class:
public Object clone( )
{ // Clone a Location object.
Location answer;
try
{
answer = (Location) [Link]( );
}
catch (CloneNotSupportedException e)
{
throw new RuntimeException
("This class does not implement Cloneable.");
}
...
In an actual implementation, you would use the name of your own class (rather
than Location), but otherwise you should follow this pattern exactly.
Its useful to know whats happening in this pattern. The pattern starts by
declaring a local Location variable called answer. We then have this block:
try
{
answer = (Location) [Link]( );
}
This is an example of a try block. If you plan extensive use of Java exceptions,
then you should read all about try blocks in Appendix C. But for your first try
block, all you need to know is that the code in the try block is executed, and the
try block will be able to handle some of the possible exceptions that may arise in
the code. In this example, the try block has just one assignment statement:
answer = (Location) [Link]( ) . The right side of the assignment
activates a method called [Link]( ). This is actually the clone method
from Javas Object type. It checks that the Location class specifies that it
implements Cloneable, and then correctly makes a copy of the location,
assigning the result to the local variable answer.
After the try block is a sequence of one or more catch blocks. Each catch block
can catch and handle an exception that may arise in the try block. Our example
has one catch block:
catch (CloneNotSupportedException e)
{
throw new RuntimeException
("This class does not implement Cloneable.");
}
79
The complete clone implementation for the Location class looks like this,
including an indication of the likely cause of the CloneNotSupportedException:
public Object clone( )
{ // Clone a Location object.
Location answer;
try
{
answer = (Location) [Link]( );
}
catch (CloneNotSupportedException e)
{ // This exception should not occur. But if it does, it would indicate a
// programming error that made [Link] unavailable. The
// most common cause would be forgetting the
// implements Cloneable clause at the start of the class.
throw new RuntimeException
("This class does not implement Cloneable.");
}
return answer;
}
The method returns the local variable, answer, which is a Location object.
This is allowed, even though the return type of the clone method is Object. A
Java Object may be anything except the eight primitive types. It might be better
if the actual return type of the clone method was Location rather than Object.
Using Location for the return type would be more accurate and would make
the clone method easier to use (without having to put a typecast with every
usage). Unfortunately, the improvement is not allowed: The return type of the
clone method must be Object.
TIP
You could combine these into one statement: return new Location(x, y).
This creates and returns a new location, using the instance variables x and y to
initialize the new location. These instance variables come from the location that
activated the clone method, so answer will indeed be a copy of that location. This
is a nice direct approach, but the direct approach will encounter problems when we
start building new classes that are based on existing classes (See page 655).
Therefore, it is better to stick with the pattern that uses [Link] and a try/
catch block.
TIP
81
int rotations
x -2
A Location y -1.5
object
83
Location mobile
int rotations
int n
x -2
A Location y -1.5
object
Location p
The first iteration of the loop rotates the location by 90 and decreases n to 1.
The second iteration does another rotation of the location and decreases n to 0.
Now the loop ends, with these values for the variables:
Location mobile
int rotations
int n
x 2
A Location y 1.5
object
Location p
Notice the difference between the two kinds of parameters. The integer parameter n has changed to zero without affecting the actual argument rotations. On
the other hand, rotating the location p has changed the object that mobile refers
to. When the method returns, the parameters p and n disappear, leaving the
situation shown at the top of the next page.
Location mobile
int rotations
x 2
A Location y 1.5
object
Java Parameters
The eight primitive types (byte, short, int, long, char,
float, double, or boolean): The parameter is initialized
with the value of the argument. Subsequent changes to the
parameter do not affect the argument.
Reference variables: When a parameter is a reference
variable, the parameter is initialized so that it refers to the
same object as the actual argument. Subsequent changes to
this object do affect the actual arguments object.
Self-Test Exercises
17. Write some code that declares two locations: one at the origin and the
other at the coordinates x = 1 and y = 1. Print the distance between the
two locations, then create a third location that is at the midpoint between
the first two locations.
18. The locations distance method is a static method. What effect does
this have on how the method is used? What effect does this have on how
the method is implemented?
19. What is the purpose of the Java constant [Link]?
20. What is the result when you add two double numbers and the answer is
larger than the largest possible double number?
21. In the midpoint method we used the expression (p1.x/2) + (p2.x/2).
Can you think of a reason why this expression is better than
(p1.x + p2.x)/2?
22. Implement an equals method for the Throttle class from Section 2.1.
23. If you dont implement an equals method for a class, then Java automatically provides one. What does the automatic equals method do?
24. Implement a clone method for the Throttle class from Section 2.1.
25. When should a program throw a RuntimeException?
85
26. Suppose that a method has an int parameter called x, and the body of
the method changes x to zero. When the method is activated, what happens to the argument that corresponds to x?
27. Suppose that a method has a Location parameter called x, and the body
of the method activates x.rotate90( ). When the method is activated,
what happens to the argument that corresponds to x?
CHAPTER SUMMARY
In Java, object-oriented programming (OOP) is supported by implementing classes. Each class defines a collection of data, called its instance
variables. In addition, a class has the ability to include two other items:
constructors and methods. Constructors are designed to provide initial
values to the classs data; methods are designed to manipulate the data.
Taken together, the instance variables, constructors, and methods of a
class are called the class members.
We generally use private instance variables and public methods. This
approach supports information hiding by forbidding data components of a
class to be directly accessed outside of the class.
A new class can be implemented in a Java package that is provided to
other programmers to use. The package includes documentation to tell
programmers what the new class does without revealing the details of how
the new class is implemented.
A program uses a class by creating new objects of that class, and activating these objects methods through reference variables.
When a method is activated, each of its parameters is initialized. If a
parameter is one of the eight primitive types, then the parameter is initialized by the value of the argument, and subsequent changes to the parameter do not affect the actual argument. On the other hand, when a parameter
is a reference variable, the parameter is initialized so that it refers to the
same object as the actual argument. Subsequent changes to this object do
affect the actual arguments object.
Java programmers must understand how these items work for classes:
the assignment operator (x = y)
the equality test (x == y)
a clone method to create a copy of an object
an equals method to test whether two separate objects are equal to
each other
87
public Throttle( )
{
top = 1;
position = 0;
}
12. [Link]
13. Underneath your classes directory, create a
subdirectory com. Underneath com create a
subdirectory knafn. Underneath knafn create
a subdirectory statistics. Your package is
placed in the statistics subdirectory.
14. import [Link].*;
15. Java automatically imports [Link]; no
explicit import statement is needed.
21. The alternative (p1.x + p2.x)/2 has a subexpression p1.x + p2.x which could result in an
overflow.
22. Here is the implementation for the throttle:
public boolean equals(Object obj)
{
if (obj instanceof Throttle)
{
Throttle candidate = (Throttle) obj;
return
([Link]==top)
&&
([Link]==position);
}
else
return false;
}
PROGRAMMING PROJECTS
PROGRAMMING PROJECTS
Specify, design, and implement a class that
can be used in a program that simulates a
combination lock. The lock has a circular
knob, with the numbers 0 through 39 marked on the
edge, and it has a three-number combination, which
well call x, y, z. To open the lock, you must turn the
knob clockwise at least one entire revolution, stopping with x at the top; then turn the knob counterclockwise, stopping the second time that y appears at
the top; finally turn the knob clockwise again,
Programming Projects
89
y-axis
x-axis
z-axis
Coordinates of
this location:
x = 2.5
y=0
z = 2.0
The location shown in the picture has three coordinates: x = 2.5, y = 0, and z = 2.0. Include methods to
set a location to a specified point, to shift a location
a given amount along one of the axes, and to retrieve
the coordinates of a location. Also provide methods
that will rotate the location by a specified angle
around a specified axis.
To compute these rotations, you will need a bit of
trigonometry. Suppose you have a location with coordinates x, y, and z. After rotating this location by
an angle , the location will have new coordinates,
which well call x' , y' , and z' . The equations for the
new coordinates use the [Link] methods
[Link] and [Link], as shown here:
After a rotation around the x-axis:
x' = x
y' = y cos ( ) z sin ( )
z' = y sin ( ) + z cos ( )
After a rotation around the y-axis:
x' = x cos ( ) + z sin ( )
y' = y
z' = x sin ( ) + z cos ( )
After a rotation around the z-axis:
x' = x cos ( ) y sin ( )
y' = x sin ( ) + y cos ( )
z' = z
-8 -6
-3 -1 1
C# D#
F# G# A#
-9 -7 -5 -4 -2 0
Note
numbers
for the
octave of
middle C
Programming Projects
2 ( 3 ) + 8 ( 3 ) + 6 = 0
There are six rules for finding the real roots of a quadratic expression:
(1) If a, b, and c are all zero, then every value of
x is a real root.
(2) If a and b are zero, but c is nonzero, then there
are no real roots.
(3) If a is zero, and b is nonzero, then the only
real root is x = c b .
(4) If a is nonzero and b 2 < 4ac , then there are
no real roots.
(5) If a is nonzero and b 2 = 4ac , then there is
one real root x = b 2 a .
(6) If a is nonzero, and b 2 > 4ac , then there are
two real roots:
2
b b 4ac
x = -------------------------------------2a
2
b + b 4ac
x = -------------------------------------2a
Write a new method that returns the number of real
roots of a quadratic expression. This answer could
be 0, or 1, or 2, or infinity. In the case of an infinite
number of real roots, have the method return 3. (Yes,
we know that 3 is not infinity, but for this purpose it
is close enough!) Write two other methods that calculate and return the real roots of a quadratic expression. The precondition for both methods is that the
expression has at least one real root. If there are two
real roots, then one of the methods returns the smaller of the two roots, and the other method returns the
larger of the two roots. If every value of x is a real
root, then both methods should return zero.
Specify, design, and implement a class that
can be used to simulate a lunar lander, which
is a small spaceship that transports astronauts from lunar orbit to the surface of the moon.
When a lunar lander is constructed, the following
items should be initialized as follows:
10
91
In this project you will design and implement a class that can generate a sequence
of pseudorandom integers, which is a
sequence that appears random in many ways. The
approach uses the linear congruence method, explained below. The linear congruence method starts
with a number called the seed. In addition to the
seed, three other numbers are used in the linear congruence method, called the multiplier, the increment, and the modulus. The formula for generating
11
This formula uses the Java % operator, which computes the remainder from an integer division.
Each time a new random number is computed,
the value of the seed is changed to that new number.
For example, we could implement a pseudorandom
number generator with multiplier = 40, increment = 3641, and modulus = 729. If we choose the
seed to be 1, then the sequence of numbers will proceed as shown here:
First number
Programming Projects
12
Run some experiments to determine the distribution of numbers returned by the new
pseudorandom method from the previous
project. Recall that this method returns a double
number in the range [0..1). Divide this range into
ten intervals, and call the method one million times,
producing a table such as shown here:
13
Range
[0.0..0.1)
[0.1..0.2)
[0.2..0.3)
[0.3..0.4)
[0.4..0.5)
[0.5..0.6)
[0.6..0.7)
[0.7..0.8)
[0.8..0.9)
[0.9..1.0)
Number of Occurrences
99889
100309
100070
99940
99584
100028
99669
100100
100107
100304
93
10% of the numbers in each interval. A pseudorandom number generator with this equal-interval behavior is called uniformly distributed.
This project is a continuation of the previous
project. Many applications require pseudorandom number sequences that are not uniformly distributed. For example, a program that
simulates the birth of babies can use random numbers for the birth weights of the newborns. But these
birth weights should have a Gaussian distribution.
In a Gaussian distribution, numbers form a bellshaped curve in which values are more likely to fall
in intervals near the center of the overall distribution. The exact probabilities of falling in a particular
interval can be computed by knowing two numbers:
(1) a number called the variance, which indicates
how widely spread the distribution appears, and (2)
the center of the overall distribution, called the median. For this kind of distribution, the median is
equal to the arithmetic average (the mean) and equal
to the most frequent value (the mode).
Generating a pseudorandom number sequence
with an exact Gaussian distribution can be difficult,
but there is a good way to approximate a Gaussian
distribution using uniformly distributed random
numbers in the range [0..1). The approach is to
generate three pseudorandom numbers r 1 , r 2 , and
r 3 , each of which is in the range [0..1). These
numbers are then combined to produce the next
number in the Gaussian sequence. The formula to
combine the numbers is:
14