Chapter13 2
Chapter13 2
Chapter 13
Java provides a plethora of predefined classes that programmers can use to build sophisticated programs. If you have
a choice of using a predefined standard class or devising your own custom class that provides the same functionality,
choose the standard class. The advantages of using a standard class include:
• The effort to produce the class is eliminated entirely; you can devote more effort to other parts of the applica-
tion’s development.
• If you create your own custom class, you must thoroughly test it to ensure its correctness; a standard class,
while not immune to bugs, generally has been subjected to a complete test suite. Standard classes are used
by many developers and thus any lurking errors are usually exposed early; your classes are exercised only by
your code, and errors may not be manifested immediately.
• Other programmers may need to modify your code to fix errors or extend its functionality. Standard classes
are well known and trusted; custom classes, due to their limited exposure, are suspect until they have been
thoroughly examined. Standard classes provide well-documented, well-known interfaces; the interfaces of
custom classes may be neither.
Software development today is increasingly component-based. In the hardware arena, a personal computer is
built by assembling
• a motherboard (a circuit board containing sockets for a microprocessor and assorted support chips),
• memory boards,
• a video card,
• a disk controller,
• a disk drive,
• a case,
• a keyboard,
• a mouse, and
• a monitor.
The video card is itself a sophisticated piece of hardware containing a video processor chip, memory, and other
electronic components. A technician does not need to assemble the card; the card is used as is off the shelf. The
video card provides a substantial amount of functionality in a standard package. One video card can be replaced
with another card from a different vendor or with another card with different capabilities. The overall computer will
work with either card (subject to availability of drivers for the OS).
Software components are used like hardware components. A software system can be built largely by assem-
bling pre-existing software building blocks. A class is a simple software component; more sophisticated software
components can consist of collections of collaborating classes. Java’s standard classes are analogous to off-the-shelf
hardware components.
13.1 Packages
Java includes a multitude of classes. These classes are organized into collections called packages. In order to speed
compilation and class loading during execution, the compiler normally has access to a small subset of the standard
classes. These common classes include String and System (as in System.out.println()) and are located in a
package called java.lang. Other classes, some of which we consider in this chapter, require a special statement at
the top of the source code that uses these classes:
import java.util.Random;
This import statement makes the definition of the Random class available to the compiler. The Random class is used
to make random number generators which are useful for games and some scientific applications. The Random class
is explored in § 13.6. The Random class is part of the java.util package of classes that contain utility classes useful
for a variety of programming tasks. Access to the definition of Random enables the compiler to verify that client code
uses Random objects properly. Without the import statement that compiler will flag as errors any statements that use
Random.
Random, String, and System are simple class names. The full class name includes the package in which the
class is a member. The full class names for the above classes are java.util.Random, java.lang.String, and
java.lang.System. If full names are used all the time, no import statements are required; however, most pro-
grammers will import the classes so the shorter simple names can be used within a program. Full names are required
when two classes with the same name from different packages are to be used simultaneously.
Multiple import statements may used to import as many class definitions as required. If present, these import
statements must appear before any other lines in the source code, except for comments.
We have seen the print(), printf(), and println() methods provided by the out public class constant of the
System class. The System class also provides a number of other services, one of which is a class method named
currentTimeMillis(). The method System.currentTimeMillis() gets information from the operating sys-
tem’s clock to determine the number of milliseconds since midnight January 1, 1970. By calling the method twice
and comparing the two results we can determine the time elapsed between two events. Try the following interactive
session:
Interactions
Welcome to DrJava. Working directory is /Users/rick/java
> long start = System.currentTimeMillis();
> System.currentTimeMillis() - start
30166
Type in the first statement and, just before hitting Enter , note the time on a clock. Go ahead an type the second
line but wait to hit the Enter key until about 30 seconds has elapsed since last pressing the Enter key (if you are a
1
slow typist, you may want to wait a full minute). The value displayed is the number of milliseconds ( 1000 seconds)
between the two Enter presses.
Using System.currentTimeMillis() interactively to time human user activity is rather limited, but System-
.currentTimeMillis() can be very useful to time program execution and synchronizing program activity with
particular time periods. Stopwatch ( 13.1) implements a software stopwatch that we can use to time program
execution:
We have been using strings so much they may seem like primitive types, but strings are actually String ob-
jects. A string is a sequence of characters. In Java, a String object encapsulates a sequence of Unicode (see
http://www.unicode.org) characters and provides a number of methods that allow programmers to use strings
effectively. Since String is a member of the java.lang package, no import is necessary.
A string literal is a sequence of characters enclosed within quotation marks, as in
"abcdef"
String w = "abcdef";
assigns the String object reference variable w to the String object "abcdef". To make a copy of this literal string,
the new operator must be used:
This assignment directs w to refer to a distinct object from the literal "abcdef" object, but the characters that make
up the two strings will be identical.
Table 13.1 lists some of the more commonly used methods of the String class.
• The statement
System.out.println("abcdef".length());
str a b c d e f
0 1 2 3 4 5
str.charAt(2) == ’c’
• Observe that none of the methods used in StringExample modify the contents of str. The expression
str.toUpperCase()
returns a new string object with all capital letters; it does not modify str. In fact, none of the methods in the
String class modify the string object upon which they are invoked. A string object may not be modified after
it is created. Such objects are called immutable. It is acceptable to modify a reference, as in:
str = str.toUpperCase();
This statement simply reassigns str to refer to the new object returned by the method call. It does not modify
the string object to which str was originally referring. Figure 13.2 illustrates.
One additional useful String method is format(). It is a class (static) method that uses a format string like
System.out.printf(). Whereas System.out.printf() displays formatted output, String.format() instead
produces a string, as demonstrated in the following interaction:
Interactions
Welcome to DrJava. Working directory is /Users/rick/java
> double x = 3.69734;
> x
3.69734
> String.format("%.2f", x)
"3.70"
str a b c d e f
0 1 2 3 4 5
str = str.toUpperCase();
str a b c d e f
0 1 2 3 4 5
ABCD E F
0 1 2 3 4 5
Figure 13.2: String reassignment. The reference is redirected to point to a different string object; the original string
object is unaffected (but it may be garbage collected if it is no longer referenced elsewhere).
The %.2f control code specifies a floating point number rounded to two decimal places. The control codes used
in the String.format() format string are exactly those used the System.out.printf() format string. This is
because the two methods use a common formatting object (of class java.util.Formatter) to do their work.
Java’s distinction between primitive types and reference types dilutes its object-orientation. Some OO languages,
like Smalltalk, have no primitive types—everything is an object. As we’ll discover, it is often convenient to treat a
primitive type as if it were an object.
The java.lang package provides a “wrapper” class for each primitive type. These wrapper classes serve two
roles:
1. They can be used to wrap a primitive type value within an object. This objectifies the primitive value and adds
a number of useful methods that can act on that value. The state of the object is defined solely by the value of
the primitive type that it wraps.
2. They provide services or information about the primitive type they wrap. For example:
• The minimum and maximum values that the primitive type can represent are available.
• Conversions to and from the primitive type’s human-readable string representations can be performed.
Public constant fields MIN_VALUE and MAX_VALUE store, respectively, the smallest and largest values that can be
represented by the int type. The Integer constructor is overloaded and accepts either a single int or single String
reference as a parameter. IntegerTest ( 13.3) shows how the Integer class can be used.
i = i1 . intValue ();
System . out . println ("i = " + i ); // Prints 500
i = i2 . intValue ();
System . out . println ("i = " + i ); // Prints 200
i = Integer . parseInt (" 100 " );
System . out . println ("i = " + i ); // Prints 100
i1 = Integer . valueOf (" 600 " );
i = i1 . intValue ();
System . out . println ("i = " + i ); // Prints 600
i = Integer . valueOf (" 700 " ). intValue ();
System . out . println ("i = " + i ); // Prints 700
}
}
A technique called autoboxing allows a primitive type to be converted automatically to an object of its wrapper
type (boxing), and it allows an object of a wrapper type to be converted automatically to its corresponding primitive
value (unboxing). For example, the compiler converts the following statement
into
Boxing and unboxing is performed automatically during parameter passing and method returns as well. A method
that expects a wrapper type object will accept a compatible primitive type and vice-versa. Autoboxing largely
eliminates the need to use explicit constructor calls and methods such as intValue(). These methods are present
because earlier versions of Java did not support autoboxing.
The Math class contains mathematical methods that and provide much of the functionality of a scientific calculator.
Table 13.4 lists only a few of the available methods.
All the methods in the Math class are class methods (static), so they can be invoked without the need to create
a Math object. In fact, the Math class defines a private constructor, so it is impossible to create a Math instance
(§ 16.2). Two public class constants are also available:
Figure 13.3 shows a problem that can be solved using methods found in the Math class. A point (x, y) is to be
rotated about the origin (point (0, 0)), while the point (px , py ) is fixed. The distance between the moving point and
the fixed point at various intervals is to be displayed in a table.
(x 2 ,y 2 )
d2
θ
(x 1 ,y 1 ) d1
(0,0) (px ,py )
Figure 13.3: A problem that can be solved using methods in the Math class.
Point (px , py ) could represent a spacecraft at a fixed location relative to a planet centered at the origin. The
moving point could then represent a satellite orbiting the planet. The task is to compute the changing distance
between the spacecraft and the satellite as the satellite orbits the planet. The spacecraft is located in the same plane
as the satellite’s orbit.
Two problems must be solved, and facts from mathematics provide the answers:
1. Problem: The location of the moving point must be recomputed as it orbits the origin.
Solution: Given an initial position (x1 , y1 ) of the moving point, a rotation of θ degrees around the origin will
yield a new point at
x2 = x1 cos θ − y1 sin θ
y2 = x1 sin θ + y1 cos θ
2. Problem: The distance between the moving point and the fixed point must be recalculated as the moving point
moves to a new position.
Solution: The distance d1 between two points (px , py ) and (x1 , y1 ) is given by the formula
q
d1 = (x1 − px )2 + (y1 − py )2
OrbitDistanceTable ( 13.4) uses these mathematical results to compute a partial orbit distance table.
Listing 13.4: OrbitDistanceTable—Generates a partial table of the orbital position and distance data
Instances of the java.util.Random class are used to generate random numbers. Random numbers are useful in
games and simulations. For example, many board games use a die (one of a pair of dice) to determine how many
places a player is to advance. A software adaptation of such a game would need a way to simulate the random roll
of a die.
All algorithmic random number generators actually produce pseudorandom numbers, not true random numbers.
A pseudorandom number generator has a particular period, based on the nature of the algorithm used. If the gen-
erator is used long enough, the pattern of numbers produced repeats itself exactly. For example, a particularly poor
pseudorandom number generator with a very short period may generate the sequence:
This repetition of identical values disqualifies all algorithmic generators from being true random number gener-
ators. The good news is that all practical algorithmic pseudorandom number generators have much longer periods.
Java’s standard pseudorandom number generator is based on Donald Knuth’s widely used linear congruential
algorithm [3]. It works quite well for most programming applications requiring random numbers. The algorithm is
given a seed value to begin, and a formula is used to produce the next value. The seed value determines the sequence
of numbers generated; identical seed values generate identical sequences.
The Random class has an overloaded constructor:
• Random()—the no parameter version uses the result of System.currentTimeMillis() (the number of mil-
liseconds since January 1, 1970) as the seed. Different executions of the program using a Random object
created with this parameter will generate different sequences since the constructor is called at different times
(unless the system clock is set back!).
• Random(long seed)—the single parameter version uses the supplied value as the seed. This is useful during
development since identical sequences are generated between program runs. When changes are made to a
malfunctioning program, reproducible results make debugging easier.
double nextDouble()
Returns the next uniformly distributed pseudorandom number in the generator’s sequence
converted to a double.
void setSeed(long seed)
Resets the seed value.
To see how the Random class might be used, consider a die. A die is a cube containing spots on each of its six
faces. The number of spots vary in number from one to six. The word die is the singular form of dice. Figure 13.4
shows a pair of dice.
Figure 13.4: A pair of dice. The top face of the left die is one, and the top face of the right die is two.
Dice are often used to determine the number of moves on a board game and are used in other games of chance.
A player rolls a die or sometimes a pair of dice, and the side(s) that face up have meaning in the game being played.
The value of a face after a roll is determined at random by the complex tumbling of the die.
DieGame ( 13.5) simulates rolling a pair of dice.
Initially:
+-------+
| * |
| |
| * |
+-------+
+-------+
| * * |
| * |
| * * |
+-------+
After rolling:
+-------+
| * * * |
| |
| * * * |
+-------+
+-------+
| * * |
| * |
| * * |
+-------+
DieGame uses objects of the Die class ( 13.6). As DieGame shows, a Die object must:
• be able to show its top face in a text-based graphical fashion (rather than just printing the number of spots).
The client code, DieGame, is shielded from the details of the Random class. A Random object is encapsulated
within the Die object. Observe that within the Die class:
• The value field is an instance variable. Each Die object must maintain its own state, so value cannot be
shared (that is, it cannot be a class variable).
• The random field is a class variable. The random variable references a Random object and is used to generate
the pseudorandom numbers that represent the visible face after a roll. This object can be shared by all dice,
since each call to its nextInt() method simply returns the next pseudorandom number in the sequence. The
next value of the value field for a particular die is not and should not be influenced by the current state of that
die. While it is not logically incorrect to make random an instance variable, it would be an inefficient use of
time and space since
1. Each time a new Die object is created, a new Random object must also be created. Object creation is a
relatively time-consuming operation.
2. Each Random object associated with each Die object occupies memory. If only one Random object is
really required, creating extra ones wastes memory.
• The random field is initialized when declared, so it will be created when the JVM loads the class into memory.
• The constructor automatically initializes value to be a random number in the range 1, 2, 3, . . . , 6 by calling
the roll() method. Even though roll() contains only one statement that could be reproduced here, this
approach avoids duplicating code. In terms of maintainability this approach is better, since if in the future the
nature of a roll changes (perhaps a ten-sided die), code only needs to be modified in one place.
If no constructor were provided, value would by default be assigned to zero, not a valid value since all faces
have at least one spot on them.
• The statement
value = random.nextInt(6) + 1;
assigns a number in the range 1, 2, . . . , 6, since the expression random.nextInt(6) itself returns a number in
the range 0, 1, . . . , 5.
13.7 Summary
13.8 Exercises
1. Devise a new type of traffic light that has TrafficLightModel ( 10.1) as its superclass but does something
different from TurnLightModel ( 11.1). Test your new class in isolation.
2. Create a new intersection class (subclass of Intersection ( 10.4)) Test your new type of intersection.