Java 1.1 Unleashed
- 11 -
|
Class | Description |
BitSet | Implements a collection of binary values |
Calendar | Used to implement a calendar |
Date | Used for date and time data storage and use |
Dictionary | Used to store a collection of key and value pairs |
GregorianCalendar | Used to implement a Gregorian calendar |
Hashtable | Used to store a hash table |
Locale | Used to implement a location |
Observable | Used to store observable data |
Properties | Used for storage of a properties list that can be saved |
Random | Used to generate a pseudo-random number |
SimpleTimeZone | Used to implement a simplified time zone |
Stack | Used to store and implement a stack |
StringTokenizer | Used to tokenize a string |
TimeZone | Used to store information about a time zone |
Vector | Used to store a vector data type |
One would expect that the Vector class would eliminate the necessity for creating your own data structures. But there may be times when you may want to conserve space to the maximum or access your data in a specialized way. In these cases, there is a technique to implement such data structures in Java.
As you know, Java has no pointers. Because dynamically linked lists and queues are implemented using pointers, is it then impossible to create these two data structures in Java? Not quite. Just as with many other tasks in Java, you need to do a little "funky stepping" to get it right because the implementation of lists, queues, and other dynamic data structures is not intuitive.
To define your own dynamic data structures, you will want to make use of the fact that references to objects in Java are already dynamic. This is demonstrated and necessitated by the practices Java uses, such as interfaces and abstract implementations.
If you are accustomed to implementing dynamically linked lists or queues in C++, the format you use in Java to create your own version of these structures should seem very familiar to you. For example, the following code creates a Node class for the list that contains a string:
class Node { String Name; Node Prev; Node Next;
}
Of course, this code creates a doubly linked list, which has links backward and forward to other nodes containing strings. You could just as easily convert this type to link objects in just about any way to exhibit just about any behavior you want: queues, stacks (remember, there is already a Stack object in the class library), doubly linked lists, circular lists, binary search trees, and the list goes on.
To implement such a list, you create a DoubleList class that contains one such Node object and links strung out from there. You can use the keyword null to represent an empty object. Here is an example of the DoubleList declaration:
class DoubleList { // Declare the listhead to be of the Node type we created earlier. // Also, set it to be an empty object. Node ListHead = null; . .
}
You then create methods to act on the list, such as InsertNode() or ClearMyListJerk()--whatever you want.
You may also want to create a constructor method for the Node class that accepts parameters to set the previous and next nodes at construction time; or you may want to create a method such as SetNext() or SetNextToNull(). Either choice would work just fine.
NOTE: Out of all this you get a surprise bonus: No worry about freeing space allocated to create nodes because the Java garbage collection processes take care of all that for you. Just create objects when you need them and let Java take care of it.
The utilities package has three interfaces, but you typically use only two: Enumeration and Observer. The other interface is EventListener; it is most commonly used internally by the AWT for handling events. Programmers only rarely implement the EventListener interface. An interface is a set of methods that must be written for any class that claims to implement the interface. This arrangement provides a way to consistently use all classes that implement the interface. Following is a summary of the Enumeration and Observer interfaces:
The Enumeration interface is used for classes that can retrieve data from a list, element by element. For example, there is an Enumeration class in the utilities package that implements the Enumeration interface for use with the Vector class. This frees you from hard-core traversal of the different classes of data structures.
The Observer interface is useful in designing classes that can watch for changes that occur in other classes.
CAUTION: Some of the examples in this chapter are not applets--they are applications. Many of these data structures are best exhibited by just plain text input and output. Removing the baggage that would have come along with applets allows the examples to be simplified so that the topic being demonstrated is clearer.
When you apply any code segments from this chapter in your own applets, remember that some of the examples are not true applets; you must deal with the differences inherent between applets and applications.
The Enumeration interface specifies a set of methods used to enumerate--that is, iterate through--a list. An object that implements this interface can be used to iterate through a list only once because the Enumeration object is consumed through its use.
For example, an Enumeration object can be used to print all the elements of a Vector object, v, as follows:
for (Enumeration e=v.elements();e.hasMoreElements();)
System.out.print(e.nextElement()+" ");
The Enumeration interface specifies only two methods: hasMoreElements() and nextElement(). The hasMoreElements() method must return true if there are elements remaining in the enumeration. The nextElement() method must return an object representing the next element within the object being enumerated. The details of how the Enumeration interface is implemented and how the data is represented internally are left up to the implementation of the specific class.
The Observer interface, if implemented by a class, allows an object of the class to observe other objects of the class Observable. The Observer interface is notified whenever the Observable object that it is watching changes.
The interface only specifies one method, update(Observable, Object). This method is called by the observed object to notify the Observer of changes. A reference to the observed object is passed along with any additional object that the observed object wants to pass to the Observer. The first argument enables the Observer to operate on the observed object; the second argument is used to pass information from the observed to the Observer.
The utilities package supplies many different classes that provide a wide variety of functionality. Although these classes don't generally have much in common, they all provide support for the most common data structures used by programmers. The techniques described in the next sections enable you to create your own specialized classes to supplement those missing from the package.
The classes supplied in the java.util package, however limited they are, do provide a great advantage over other languages. The main advantage is that these classes simplify some things and eliminate a lot of the garbage you were stuck with in the past, in terms of freeing memory and doing mundane programming tasks.
However, there are a number of limitations. For example, you have to "dance a little bit" to implement some of the more complicated data structures. And if you want speed, there are much faster languages to choose from. Java provides a combination of power and simplicity while sacrificing speed. However, don't worry that your programs will be slugs. Although Java is not nearly as efficient as C++ and C, it still beats Visual Basic in terms of size and speed.
The BitSet class implements a data type that represents a collection of bits. The collection grows dynamically as more bits are required. The class is useful for representing a set of true and false values. Specific bits are identified using nonnegative integers. The first bit is bit 0.
The BitSet class is most useful for storing a group of related true/false values, such as user responses to Yes and No questions. For example, if the applet has a number of radio buttons, you can slap those values into an instance of the BitSet class.
The class is also useful in terms of bitmapping your own graphics. You can create bitsets that represent a pixel at a time (of course, it would be much easier to use the Graphics class for this purpose instead).
Individual bits in the set are turned on or off with the set() and clear() methods. Individual bits are queried with the get() method. These methods all take the specific bit number as their only argument. The basic boolean operations AND, OR, and XOR can be performed on two bitsets using the and(), or(), and xor() methods. Because these methods modify one of the bitsets, you generally use the clone() method to create a duplicate of one bitset, and then use the AND, OR, or XOR operation on the clone with the second bitset. The result of the operation then ends up in the cloned bitset. The BitSet1 program in Listing 11.1 shows the basic BitSet operations.
import java.io.DataInputStream; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.BitSet; class BitSet1 { public static void main(String args[]) throws java.io.IOException { BufferedReader dis=new BufferedReader(new InputStreamReader(System.in)); String bitstring; BitSet set1,set2,set3; set1=new BitSet(); set2=new BitSet(); // Get the first bit sequence and store it System.out.println("Bit sequence #1:"); bitstring=dis.readLine(); for (short i=0;i<bitstring.length();i++){ if (bitstring.charAt(i)=='1') set1.set(i); else set1.clear(i); } // Get the second bit sequence and store it System.out.println("Bit sequence #2:"); bitstring=dis.readLine(); for (short i=0;i<bitstring.length();i++){ if (bitstring.charAt(i)=='1') set2.set(i); else set2.clear(i); } System.out.println("BitSet #1: "+set1); System.out.println("BitSet #2: "+set2); // Test the AND operation set3=(BitSet)set1.clone(); set3.and(set2); System.out.println("set1 AND set2: "+set3); // Test the OR operation set3=(BitSet)set1.clone(); set3.or(set2); System.out.println("set1 OR set2: "+set3); // Test the XOR operation set3=(BitSet)set1.clone(); set3.xor(set2); System.out.println("set1 XOR set2: "+set3); }
}
The output from this program looks like this:
Bit sequence #1: 1010 Bit sequence #2: 1100 BitSet #1: {0, 2} BitSet #2: {0, 1} set1 AND set2: {0} set1 OR set2: {0, 1, 2} set1 XOR set2: {1, 2}
Table 11.2 summarizes all the methods available in the BitSet class.
Method | Description |
Constructors |
|
BitSet() | Constructs an empty BitSet |
BitSet(int) | Constructs an empty BitSet of a given size |
Methods |
|
and(BitSet) | Logically ANDs the object's bitset with another BitSet object |
clear(int) | Clears a specific bit |
clone() | Creates a clone of the BitSet object |
equals(Object) | Compares this object against another BitSet object |
get(int) | Returns the value of a specific bit |
hashCode() | Returns the hash code |
or(BitSet) | Logically ORs the object's bitset with another BitSet object |
set(int) | Sets a specific bit |
size() | Returns the size of the set |
toString() | Converts bit values to a string representation |
xor(BitSet) | Logically XORs the object's bitset with another BitSet object |
The Calendar class is an abstract class used to convert dates. You can use this class to convert a Date object to fields, such as YEAR, MONTH, HOUR, and so on. You can also use these fields to update a Date object.
In the API definition, only one subclass of Calendar exists: GregorianCalendar. Because most people and virtually all businesses in the world use the Gregorian calendar, you will find all the details about calendars in that section, later in this chapter.
Table 11.3 summarizes the methods available in the Calendar class.
Method | Description |
Constructors |
|
Calendar() | Creates a calendar with the default TimeZone and Locale |
Calendar(TimeZone,Locale) | Creates a calendar with the given TimeZone and Locale |
Static Methods |
|
getDefault() | Returns a calendar with the default TimeZone and Locale |
getDefault(TimeZone) | Returns a calendar with the default Locale and given TimeZone |
getDefault(Locale) | Returns a calendar with the given Locale and default TimeZone |
getDefault(TimeZone,Locale) | Returns a calendar with the given TimeZone and Locale |
getAvailableLocales() | Returns an array of all available locales |
Methods |
|
getTime() | Returns the date and time |
setTime(Date) | Sets the date and time |
get(byte) | Returns the specified field |
set(byte,int) | Sets the specified field to the specified value |
set(int,int,int) | Sets the year, month, and date |
set(int,int,int,int,int) | Sets the year, month, date, hour, and minute |
set(int,int,int,int,int,int) | Sets the year, month, date, hour, minute, and second |
clear() | Clears all fields |
clear(byte) | Clears the specified field |
isSet(int) | Returns true if the specified field is set |
equals(Object) | Returns true if two objects are the same |
before(Object) | Returns true if this object is before the given object |
after(Object) | Returns true if this object is after the given object |
add(byte,int) | Adds the given value to the field |
roll(byte,boolean) | Increments or decrements (depending on the boolean value) the specified field by one unit |
setTimeZone(TimeZone) | Sets the time zone this object is in |
setValidationMode(boolean) | If the boolean value is set to true, invalid dates are allowed |
getValidationMode() | Returns whether or not invalid dates are allowed |
setFirstDayOfWeek(byte) | Sets the first day of the week |
getFirstDayOfWeek() | Returns the first day of the week |
setMinimumDaysInFirstWeek(byte) | Sets how many days are required to define the first week of the month |
getMinimumDaysInFirstWeek() | Returns how many days are required to define the first week of the month |
getMinimum(byte) | Returns the minimum possible value for the given field |
getMaximum(byte) | Returns the maximum possible value for the given field |
getGreatestMinimum(byte) | Returns the greatest possible minimum value for the field |
getLeastMaximum(byte) | Returns the least possible maximum value for the given field |
Clone() | Makes a copy of this object |
Before Java 1.1, the Date class was an extremely important class for handling dates and times. However, it was weak in some areas such as internationalization and dealing with the differences in daylight saving time in different locations.
With Java 1.1, the Date class has been relegated to just storing the exact time. Most of the old Date class methods have been deprecated and should no longer be used. All the methods for converting time between binary and human-readable form are now handled by the Calendar, GregorianCalendar, TimeZone, SimpleTimeZone, and Locale classes. An example of how these classes can work together is found in Listing 11.3, in "The GregorianCalendar Class," later in this chapter.
The default constructor is used when the current date and time are required. The other constructor takes a millisecond representation of time and creates a Date object based on it.
When a date is converted to a string by an automatic coercion, the toString() method is used. The resulting string returned by the toString() function follows UNIX time and date standards.
Dates can be compared to each other by using their UTC values (the UTC value is the number of seconds since January 1, 1970) or by using the methods after(), before(), and equals().
CAUTION: Don't try to launch space shuttles or coordinate nuclear attacks based on your operating system's local time as reflected by Java. Although the API is intended to reflect UTC (Coordinated Universal Time), it doesn't do so exactly. This inexact behavior is inherited from the time system of the underlying OS. The vast majority of all modern operating systems assume that one day equals 3600 seconds/hour multiplied by 24 hours, and as such, they reflect time to the accuracy that UTC does.
Under the UTC, about once a year, there is an extra second, called a "leap second," added to account for the wobble of the earth. Most computer clocks are not accurate enough to reflect this distinction.
Between UTC and standard OS time (UT/GMT), there is this subtle difference; one is based on an atomic clock and the other is based on astronomical observations, which, for all practical purposes, is an invisibly fine hair to split.
For more information, Sun suggests you visit the U.S. Naval Observatory site, particularly the Directorate of Time at http://tycho.usno.navy.mil and its definitions of different systems of time at http://tycho.usno.navy.mil/systime.html.
Table 11.4 summarizes all the nondeprecated methods available in the Date class. You can find the deprecated methods by looking at the API reference included with the JDK or found on JavaSoft's Web site (http://www.javasoft.com/).
Method | Description |
Constructors |
|
Date() | Constructs a date using today's date and time |
Date(long) | Constructs a date using a single UTC value |
Methods |
|
after(Date) | Returns true if the date is later than the specified date |
before(Date) | Returns true if the date is earlier than the specified date |
equals(Object) | Returns true if the date and the specified date are equal |
getTime() | Returns the time as a single UTC value |
hashCode() | Computes a hash code for the date |
setTime(long) | Sets the time using a single UTC value |
toString() | Converts a date to text using UNIX ctime() conventions |
The sample applet in Listing 11.2 demonstrates the use of the Date class.
import java.awt.*; import java.util.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class MichaelSimpleClock extends java.applet.Applet { Button DateButton = new Button( " Click me! "); public void init() { ButtonListener bl = new ButtonListener(DateButton); add(DateButton); DateButton.addActionListener(bl); } } class ButtonListener implements ActionListener { Date TheDate = new Date(); Button theButton; public ButtonListener(Button aButton) { theButton = aButton; } public void actionPerformed (ActionEvent e) { theButton.setLabel(TheDate.toString()); } }
Figure 11.1 shows the MichaelSimpleClock applet. Note that the clock in the applet is wrong: it is not actually 8:00 A.M. There is no way I would write that early in the morning!
The MichaelSimpleClock applet.
What about a real-time clock that updates as the clock changes? To accomplish this small feat, you must include in the applet a loop that has each iteration reconstructing the internal Date instance. Then you regularly repaint that value inside the applet's paint() method. You also have to include threading to keep your system from locking up during the applet's execution. Threading is covered in Chapter 6, "Threads and Multithreading," so a real-time clock was not included in this chapter.
Before 1582, the western world used the Julian calendar system, which was adopted by Julius Caesar. The problem with the Julian calendar was that it had too many leap years (3 too many every 400 years). Pope Gregory XIII fixed that problem. In addition, the Julian year started on March 25; the Gregorian calendar year starts on January 1. At the time of the switch, the last day of the Julian calendar was October 4, 1582, and the first day of the Gregorian calendar was October 15, 1582. The dates in between never occurred. However, this was not true for England and America, which kept the Julian calendar until 1752. When England and America switched, the last Julian day was September 2, 1752, and the first Gregorian date was September 14, 1752. If you are working on historical dates, I strongly recommend that you do some additional reading on this subject!
The GregorianCalendar class handles conversions between actual dates and data fields such as month for the Gregorian calendar. The Gregorian calendar is the most widely used calendar system in the world. However, in fact, this class also handles the Julian calendar system.
Table 11.5 summarizes the methods available in the GregorianCalendar class.
Method | Description |
Constructors |
|
GregorianCalendar() | Creates a Gregorian calendar using the current time with the default TimeZone and Locale |
GregorianCalendar(TimeZone) | Creates a Gregorian calendar using the current time with the default Locale and the given TimeZone |
GregorianCalendar(TimeZone,Locale) | Creates a Gregorian calendar with the current time and the given TimeZone and Locale |
GregorianCalendar(int,int,int) | Creates a Gregorian calendar at the given year, month, and date with the default TimeZone and Locale |
GregorianCalendar(int,int,int,int,int) | Creates a Gregorian calendar at the given year, month, date, hour, and minute with the default TimeZone and Locale |
GregorianCalendar(int,int,int,int,int,int) | Creates a Gregorian calendar at the given year, month, date, hour, minute, and second with the default TimeZone and Locale |
GregorianCalendar(Locale) | Creates a Gregorian calendar at the current time with the default TimeZone and the given Locale |
Methods |
|
setGregorianChange(Date) | Sets the date that the Julian calendar changed to the Gregorian calendar |
getGregorianChange() | Returns the date that the Julian calendar changed to the Gregorian calendar |
isLeapYear() | Returns true if the year of this object is a leap year |
equals(Objects) | Returns true if the two objects are equal |
before(Object) | Returns true if this object is before the date of the object given |
after(Object) | Returns true if the object is after the date of the object given |
add(byte,amount) | Adds the amount given to the field specified by byte |
roll(byte,boolean) | Increments or decrements (depending on the boolean value) the specified field by one unit |
getMinimum(byte) | Returns the minimum value for the specified field |
getMaximum(byte) | Returns the maximum value for the specified field |
getGreatestMinimum(byte) | Returns the greatest minimum value for the specified field |
getLeastMaximum(byte) | Returns the least maximum value for the specified field |
clone() | Makes a copy of this object |
import java.util.*; public class cal { public static void main(String args[]) { int msecsInHour = 60*60*1000; SimpleTimeZone cst = new SimpleTimeZone(-6*msecsInHour,"CST"); cst.setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2*msecsInHour); cst.setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*msecsInHour); Calendar calendar = new GregorianCalendar(cst); System.out.println("Day of Week: " + calendar.get(Calendar.DAY_OF_WEEK)); }
}
The Locale class is used to define a locale. A locale is a combination of a geographic location, a language, and a variant. The variant is usually a browser-specific code. The Locale class is key in using internationalization in your Java code. The Locale class enables you to write your program independent of country and language. Specific behaviors for each country and language can easily be added later when they are needed.
The ISO (International Standards Organization) language codes are documented in standard ISO-639 (http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt). The ISO country codes are documented in standard ISO-3166 (http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html). An example of a country code is "US" (for the United States). An example of a language code is "EN" (for English). Some example of variants are WIN (for Windows), MAC (for Macintosh), and POSIX (for UNIX).
Table 11.6 lists the methods available in the Locale class.
Method | Description |
Constructor |
|
Locale(String, String) | Creates a locale based on a given language and country |
Locale(String, String, String) | Creates a locale based on a given language, country, and variant |
Static Methods |
|
getDefault() | Returns the default Locale |
setDefault(Locale) | Sets the default Locale |
Methods |
|
getLanguage() | Returns the language in use |
getCountry() | Returns the country in use |
getISO3Language() | Returns the three-character ISO code for the language |
getISO3Country() | Returns the three-character ISO code for the country |
getVariant() | Returns the variant in use |
toString() | Returns the object as a String |
getDisplayLanguage() | Returns the language to display to the user |
getDisplayLanguage(Locale) | Returns the language to display to the user based on the given Locale |
getDisplayCountry() | Returns the country to display to the user |
getDisplayCountry(Locale) | Returns the country to display to the user for the given Locale |
getDisplayVariant() | Returns the variant to display to the user |
getDisplayVariant(Locale) | Returns the variant to display to the user for the given Locale |
getDisplayName() | Returns the language, country, and variant to display to the user |
getDisplayName(Locale) | Returns the language, country, and variant to display to the user for the given Locale |
clone() | Makes a copy of this locale |
hashCode() | Returns the hash code for this locale |
equals(Object) | Returns true if two locales are equal |
For the programming of games and many other program types, it is important to be able to generate random numbers. Java includes the capability to generate random numbers efficiently and effectively.
The Random class implements a pseudo-random number data type that generates a stream of seemingly random numbers. To create a sequence of different pseudo-random values each time the application is run, create the Random object as follows:
Random r=new Random();
This statement seeds the random generator with the current time. On the other hand, consider the following statement:
Random r=new Random(326); // Pick any value
This statement seeds the random generator with the same value each time, resulting in the same sequence of pseudo-random numbers each time the application runs. The generator can be reseeded at any time using the setSeed() method.
TIP: Want to get really random numbers? Well, you can't. But a common practice to simulate actual random numbers in computer programs is to seed the random number generator with some variant of the current time or date. If, for example, you want to seed a random number generator with the sum of the current seconds, minutes, and hours, you could use this code, which should suffice for most tasks:int OurSeed = ADate.getSeconds() + ADate.getHours() + ADate.getMinutes(); Random = new Random(OurSeed);
Pseudo-random numbers can be generated using one of these functions: nextInt(), nextLong(), nextFloat(), nextDouble(), or nextGaussian(). The first four functions return integers, longs, floats, and doubles. For more information about the Gaussian distribution, refer to the following sidebar. The Random1 program in Listing 11.4 prints five pseudo-random uniformly distributed values using these functions.
import java.lang.Math; import java.util.Date; import java.util.Random; class Random1 { public static void main(String args[]) throws java.io.IOException { int count=6; Random randGen=new Random(); System.out.println("Uniform Random Integers"); for (int i=0;i<count;i++) System.out.print(randGen.nextInt()+" "); System.out.println("\n"); System.out.println("Uniform Random Floats"); for (int i=0;i<count;i++) System.out.print(randGen.nextFloat()+" "); System.out.println("\n"); System.out.println("Gaussian Random Floats"); for (int i=0;i<count;i++) System.out.print(randGen.nextGaussian()+" "); System.out.println("\n"); System.out.println("Uniform Random Integers [1,6]"); for (int i=0;i<count;i++) System.out.print((Math.abs(randGen.nextInt())%6+1)+" "); System.out.println("\n"); } }
The output from the preceding program looks like this:
Uniform Random Integers 1704667569 -1431446235 1024613888 438489989 710330974 -1689521238 Uniform Random Floats 0.689189 0.0579988 0.0933537 0.748228 0.400992 0.222109 Gaussian Random Floats -0.201843 -0.0111578 1.63927 0.205938 -0.365471 0.626304 Uniform Random Integers [1,6] 4 6 1 6 3 2
If you want to generate uniformly distributed random integers within a specific range, the output from nextInt(), nextLong(), or nextDouble() can be scaled to match the required range. However, a simpler approach is to take the remainder of the result of nextInt() divided by the number of different values plus the first value of the range. For example, if the values 10 to 20 are needed, you can use the formula nextInt()%21+10. Unfortunately, although this method is much simpler than scaling the output of nextInt(), it is guaranteed to work only on truly random values. Because the pseudo-random generator may have various undesired correlations, the modulus operator may not provide acceptable results--you might get all odd numbers, for example. In other words, don't plan on simulating the detonation of your new H-bomb in Java because you may find yourself a couple miles too close.
Uniformly distributed random numbers are generated using a modified linear congruential method with a 48-bit seed. Uniformly distributed random numbers within a given range all appear with the same frequency. The Random class can also generate random numbers in a Gaussian or normal distribution. The Gaussian frequency distribution curve is also referred to as a bell curve. For information on the Gaussian frequency distribution curve, see The Art of Computer Programming, Volume 2, by Donald Knuth.
Table 11.7 summarizes the complete interface of the Random class.
Method | Description |
Constructors |
|
Random() | Creates a new random number generator |
Random(long) | Creates a new random number generator using a seed |
Methods |
|
nextDouble() | Returns a pseudo-random, uniformly distributed double |
nextFloat() | Returns a pseudo-random, uniformly distributed float |
nextGaussian() | Returns a pseudo-random, Gaussian distributed double |
nextInt() | Returns a pseudo-random, uniformly distributed integer |
nextLong() | Returns a pseudo-random, uniformly distributed long |
setSeed(long) | Sets the seed of the pseudo-random number generator |
import java.awt.*; import java.util.*; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; public class TheWanderer extends java.applet.Applet { int xpos = 100; int ypos = 100; // Our current date. Calendar C = new GregorianCalendar(); // The movement button Button theButton = new Button("Click Me"); // Our random number generator. Random R; public void init() { SimpleListener simple = new SimpleListener(this); add(theButton); theButton.addActionListener(simple); setBackground(Color.white); // Our random number generator seeded with the current seconds. int seed = C.get(Calendar.SECOND); R = new Random(seed); } public void paint(Graphics g) { g.setColor(Color.black); g.fillOval(xpos,ypos, 50, 50); } public void move() { // Move our thing. xpos = xpos + (Math.abs(R.nextInt())%10-7); ypos = ypos + (Math.abs(R.nextInt())%10-7); // repaint the sucker. repaint(); } } class SimpleListener implements ActionListener { private TheWanderer theClass; public SimpleListener(TheWanderer aClass) { theClass = aClass; } public void actionPerformed (ActionEvent e) { theClass.move(); } }
Figure 11.2 shows TheWanderer applet during its execution.
TheWanderer applet.
The SimpleTimeZone class is a simple TimeZone that can be used with the GregorianCalendar class. The SimpleTimeZone class assumes that time-zone rules have not changed historically. Additionally, it assumes that the rules are of a simple nature. Even with these limitations, this class can handle the needs of the majority of programmers. Listing 11.3, earlier in this chapter, provides an example of how to use this class. Table 11.8 summarizes the methods available with the SimpleTimeZone class.
Method | Description |
Constructor |
|
SimpleTimeZone(int,String) | Creates a TimeZone with the given offset and time-zone name |
SimpleTimeZone(int,String,int,int, | Creates a TimeZone with the given offset, |
int,int,int,int,int,int) | time-zone name, and time to start and end daylight saving time |
Methods |
|
setStartYear(int) | Sets the first year that daylight saving time started |
setStartRule(int,int,int,int) | Sets the rule for the beginning of daylight saving time based on the month, week, day of week, and time of day |
setEndRule(int,int,int,int) | Sets the rule for the end of daylight saving time based on the month, week, day of week, and time of day |
getOffset(int,int,int,int,int,int) | Returns the Greenwich mean time (GMT) offset for a given time |
getRawOffset() | Returns the GMT offset for the current time |
setRawOffset(int) | Sets the base GMT offset |
useDaylightTime() | Returns true if this TimeZone uses daylight saving time |
inDaylightTime(Date) | Returns true if the given date is in daylight saving time |
clone() | Makes a copy of this object |
hashCode() | Returns the hash code for this object |
equals(Object) | Returns true if this object is equal to the one given |
This section describes the function of the StringTokenizer class, which also could have been appropriately grouped with the other classes in Chapter 12, "The I/O Package," because it is so vital to the input and output functions demonstrated in that chapter. The StringTokenizer class enables you to parse a string into a number of smaller strings called tokens. This class works specifically for what is called "delimited text," which means that each individual substring of the string is separated by a delimiter. The delimiter can be anything ranging from an * to YabaDaba. You simply specify what you want the class to look for when tokenizing the string.
This class is included in this chapter because it has uses that prove helpful in everything from a spreadsheet applet to an arcade game applet.
The delimiter set can be specified when the StringTokenizer object is created, or it can be specified on a per-token basis. The default delimiter set is the set of whitespace characters. With this delimiter set, the class would find all the separate words in a string and tokenize them. For example, the StringTokenizer1 code in Listing 11.6 prints out each word of the string on a separate line.
import java.io.DataInputStream; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.StringTokenizer; class StringTokenizer1 { public static void main(String args[]) throws java.io.IOException { BufferedReader dis=new BufferedReader(new InputStreamReader(System.in)); System.out.println("Enter a sentence: "); String s=dis.readLine(); StringTokenizer st=new StringTokenizer(s); while (st.hasMoreTokens()) System.out.println(st.nextToken()); } }
Here is the output from this listing:
Enter a sentence: Four score and seven Four score and seven
Pure excitement. The method countTokens() returns the number of tokens remaining in the string using the current delimiter set--that is, the number of times nextToken() can be called before generating an exception. This is an efficient method because it does not actually construct the substrings that nextToken() must generate.
In addition to extending the java.lang.Object class, the StringTokenizer class implements the java.util.Enumeration interface.
Table 11.9 summarizes the methods of the StringTokenizer class.
Method | Description |
Constructors |
|
StringTokenizer | Constructs a StringTokenizer given a |
(String) | string using whitespace as delimiters |
StringTokenizer | Constructs a StringTokenizer given a |
(String, String) | string and a delimiter set |
StringTokenizer | Constructs a StringTokenizer given a |
(String, String, boolean) | string and a delimiter set; the final parameter is a boolean value which, if true, says that the delimiters must be returned as tokens (if this parameter is false, the tokens are not returned) |
Methods |
|
countTokens() | Returns the number of tokens remaining in the string |
hasMoreTokens() | Returns true if more tokens exist |
nextToken() | Returns the next token of the string |
nextToken(String) | Returns the next token, given a new delimiter set |
hasMoreElements() | Returns true if more elements exist in the enumeration |
nextElement() | Returns the next element of the enumeration using the current delimiter set |
The TimeZone class is a generic class that represents any kind of time zone. This class contains the offset from Greenwich mean time (GMT) and can deal with daylight saving time. Table 11.10 lists the methods available with the TimeZone class.
Method | Description |
Constructor |
|
TimeZone() | Creates a TimeZone |
Static Methods |
|
getTimeZone(String) | Returns the TimeZone object given the name of the time zone |
getAvailableIDs() | Returns an array of all TimeZone names |
getAvailableIDs(int) | Returns an array of all TimeZone names that apply to a particular offset |
getDefault() | Returns the default TimeZone for this machine |
setDefault(TimeZone) | Sets the default TimeZone for this machine |
Methods |
|
getOffset(int,int,int,int,int,int) | Returns the GMT offset for the local TimeZone at the time given |
getRawOffset() | Returns the GMT offset for the local TimeZone at the current time |
getID() | Returns the name of the TimeZone |
setID(String) | Sets the name of the TimeZone |
useDaylightTime() | Returns true if this TimeZone uses daylight saving time |
inDaylightTime(Date) | Returns true if the date given is in daylight saving time for this TimeZone |
clone() | Makes a copy of this object |
As stated earlier in this chapter, Java doesn't include dynamically linked lists, queues, or other data structures of that type. Instead, the designers of Java envisioned the Vector class, which handles the occasions when you need to dynamically store objects. Of course, there are positive and negative consequences of this decision by the designers at Sun. On the positive side, the Vector class contributes to the simplicity of the language. The major negative point is that, at face value, the Vector class severely limits programmers from using more sophisticated programs.
In any case, the Vector class implements a dynamically allocated list of objects. It attempts to optimize storage by increasing the storage capacity of the list when needed by increments larger than just one object. Typically, with this mechanism, there is some excess capacity in the list. When this capacity is exhausted, the list is reallocated to add another block of objects at the end of the list. Setting the capacity of the Vector object to the needed size before inserting a large number of objects reduces the need for incremental reallocation. Because of this mechanism, it is important to remember that the capacity (the available elements in the Vector object) and the size (the number of elements currently stored in the Vector object) usually are not the same.
Suppose that a Vector with capacityIncrement equal to 3 has been created. As objects are added to the Vector, new space is allocated in chunks of three objects. After five elements have been added, there is still room for one more element without the need for any additional memory allocation.
After the sixth element has been added, there is no more excess capacity. When the seventh element is added, a new allocation is made to add three additional elements, giving a total capacity of nine. After the seventh element is added, there are two remaining unused elements.
The initial storage capacity and the capacity increment can both be specified in the constructor. Even though the capacity is automatically increased as needed, the ensureCapacity() method can be used to increase the capacity to a specific minimum number of elements; the trimToSize() method can be used to reduce the capacity to the minimum number of elements needed to store the current amount. New elements can be added to the Vector using the addElement() and insertElementAt() methods. The elements passed to be stored in the Vector must be derived from type Object. Elements can be changed using the setElementAt() method. Removal of elements is accomplished with the removeElement(), removeElementAt(), and removeAllElements() methods. Elements can be accessed directly using the elementAt(), firstElement(), and lastElement() methods; elements can be located using the indexOf() and lastIndexOf() methods. Information about the size and the capacity of the Vector are returned by the size() and capacity() methods. The setSize() method can be used to directly change the size of the Vector.
For example, the Vector1 code in Listing 11.7 creates a Vector of integers by adding new elements to the end. Then, using a variety of techniques, it prints the Vector.
import java.lang.Integer; import java.util.Enumeration; import java.util.Vector; class Vector1 { public static void main(String args[]){ Vector v=new Vector(10,10); for (int i=0;i<20;i++) v.addElement(new Integer(i)); System.out.println("Vector in original order using an Enumeration"); for (Enumeration e=v.elements();e.hasMoreElements();) System.out.print(e.nextElement()+" "); System.out.println(); System.out.println("Vector in original order using elementAt"); for (int i=0;i<v.size();i++) System.out.print(v.elementAt(i)+" "); System.out.println(); // Print out the original vector System.out.println("\nVector in reverse order using elementAt"); for (int i=v.size()-1;i>=0;i++) System.out.print(v.elementAt(i)+" "); System.out.println(); // Print out the original vector System.out.println("\nVector as a String"); System.out.println(v.toString()); } }
The output from this program looks like this:
Vector in original order using an Enumeration 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Vector in original order using elementAt 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Vector in reverse order using elementAt 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Vector as a String [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Notice the use of the Enumeration object as one way to access the elements of a Vector. Look at the following lines:
for (Enumeration e=v.elements();e.hasMoreElements();) System.out.print(e.nextElement()+" ");
You can see that an Enumeration object, which represents all the elements in the Vector, is created and returned by the Vector method elements(). With this Enumeration object, the loop can check to see whether there are more elements to process using the Enumeration method hasMoreElements(); the loop can get the next element in the Vector using the Enumeration method nextElement().
The Vector2 program in Listing 11.8 shows some of the vector-accessing techniques. It first generates a vector of random integers; then it allows the user to search for a specific value. The locations of the first and last occurrences of the value are printed by the program using the indexOf() and lastIndexOf() methods.
import java.io.DataInputStream; import java.io.InputStreamReader; import java.io.BufferedReader; import java.lang.Integer; import java.lang.Math; import java.util.Enumeration; import java.util.Random; import java.util.Vector; class Vector2 { public static void main(String args[]) throws java.io.IOException { int numElements; BufferedReader dis=new BufferedReader(new InputStreamReader(System.in)); Vector v=new Vector(10,10); Random randGen=new Random(); System.out.println("How many random elements? "); numElements=Integer.valueOf(dis.readLine()).intValue(); for (int i=0;i<numElements;i++) v.addElement(new Integer(Math.abs( randGen.nextInt())%numElements)); System.out.println(v.toString()); Integer searchValue; System.out.println("Find which value? "); searchValue=Integer.valueOf(dis.readLine()); System.out.println("First occurrence is element "+ v.indexOf(searchValue)); System.out.println("Last occurrence is element "+ v.lastIndexOf(searchValue)); }
}
The output from this program looks like this:
How many random elements? 10 [0, 2, 8, 4, 9, 7, 8, 6, 3, 2] Find which value? 8 First occurrence is element 2 Last occurrence is element 6
In addition to extending the java.lang.Object class, the Vector class implements the java.lang.Cloneable interface. Table 11.11 summarizes the methods of the Vector class.
Variable | Description |
capacityIncrement | Size of the incremental allocations, in elements |
elementCount | Number of elements in Vector |
elementData | Buffer in which the elements are stored |
Method | Description |
Constructors |
|
Vector() | Constructs an empty vector |
Vector(int) | Constructs an empty vector with the specified storage capacity |
Vector(int, int) | Constructs an empty vector with the specified storage capacity and capacity increment |
Methods |
|
addElement(Object) | Adds the specified object at the end of the Vector |
capacity() | Returns the capacity of the Vector |
clone() | Creates a clone of the Vector |
contains(Object) | Returns true if the specified object is in the Vector |
copyInto(Object[]) | Copies the elements of this vector into an array |
elementAt(int) | Returns the element at the specified index |
elements() | Returns an Enumeration of the elements |
ensureCapacity(int) | Ensures that the Vector has the specified capacity |
firstElement() | Returns the first element of the Vector |
indexOf(Object) | Returns the index of the first occurrence of the specified object within the Vector |
indexOf(Object, int) | Returns the index of the specified object within the Vector, starting the search at the index specified and proceeding toward the end of the Vector |
insertElementAt(Object, int) | Inserts an object at the index specified |
isEmpty() | Returns true if the Vector is empty |
lastElement() | Returns the last element of the Vector |
lastIndexOf(Object) | Returns the index of the last occurrence of the specified object within the Vector |
lastIndexOf(Object, int) | Returns the index of the specified object within the Vector, starting the search at the index specified and proceeding toward the beginning of the Vector |
removeAllElements() | Removes all elements of the Vector |
removeElement(Object) | Removes the specified object from the Vector |
removeElementAt(int) | Removes the element with the specified index |
setElementAt(Object, int) | Stores the object at the specified index in the Vector |
setSize(int) | Sets the size of the Vector |
size() | Returns the number of elements in the Vector |
toString() | Converts the Vector to a string |
trimToSize() | Trims the Vector's capacity down to the specified size |
The stack data structure is key to many programming efforts, ranging from building compilers to solving mazes. The Stack class in the Java library implements a Last In, First Out (LIFO) stack of objects. Even though they are based on (that is, they extend) the Vector class, Stack objects are typically not accessed in a direct fashion. Instead, values are pushed onto and popped off the top of the stack. The net effect is that the values most recently pushed are the first to pop.
The Stack1 code in Listing 11.9 pushes strings onto the stack and then retrieves them. The strings end up printed in the reverse order from which they were stored.
import java.io.DataInputStream; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Stack; import java.util.StringTokenizer; class Stack1 { public static void main(String args[]) throws java.io.IOException { BufferedReader dis=new BufferedReader(new InputStreamReader(System.in)); System.out.println("Enter a sentence: "); String s=dis.readLine(); StringTokenizer st=new StringTokenizer(s); Stack stack=new Stack(); while (st.hasMoreTokens()) stack.push(st.nextToken()); while (!stack.empty()) System.out.print((String)stack.pop()+" "); System.out.println(); } }
The output from this program looks like this:
Enter a sentence: The quick brown fox jumps over the lazy dog dog lazy the over jumps fox brown quick The
Even though Stack objects normally are not accessed in a direct fashion, it is possible to search the Stack for a specific value using the search() method. search() accepts an object to find and returns the distance from the top of the Stack where the object was found. It returns -1 if the object is not found.
The method peek() returns the top object on the Stack without actually removing it from the Stack. The peek() method throws an EmptyStackException if the stack has no items.
Table 11.12 summarizes the complete interface of the Stack class.
Method | Description |
Constructor |
|
Stack() | Constructs an empty Stack |
Methods |
|
empty() | Returns true if the Stack is empty |
peek() | Returns the top object on the Stack without removing the element |
pop() | Pops an element off the Stack |
push(Object) | Pushes an element onto the Stack |
search(Object) | Finds an object on the Stack |
The Dictionary class is an abstract class used as a base for the Hashtable class. It implements a data structure that allows a collection of key and value pairs to be stored. Any type of object can be used for the keys or the values. Typically, the keys are used to find a particular corresponding value.
Because the Dictionary class is an abstract class that cannot be used directly, the code examples presented in this section cannot actually be run. They are presented only to explain the purpose and use of the methods declared by this class. The following code would, hypothetically, be used to create a Dictionary with these values:
Dictionary products = new Dictionary(); products.put(new Integer(342), "Widget"); products.put(new Integer(124), "Gadget"); products.put(new Integer(754), "FooBar");
The put() method is used to insert a key and value pair into the Dictionary. Both arguments must be derived from the class Object. The key is the first argument and the value is the second argument.
A value can be retrieved using the get() method and a specific key to be found. get() returns the null value if the specified key is not found. Here's an example:
String name = products.get(new Integer(124)); if (name != null) { System.out.println("Product name for code 124 is " + name); }
Although an individual object can be retrieved with the get() method, it
is sometimes necessary to access all the keys or all the values. Two methods, keys()
and elements(), return Enumerations that can be used to access
the keys and the values.
Table 11.13 summarizes the complete interface of the Dictionary class.
Method | Description |
Constructor |
|
Dictionary() | Constructs an empty Dictionary |
Methods |
|
elements() | Returns an Enumeration of the values |
get(Object) | Returns the object associated with the specified key |
isEmpty() | Returns true if the Dictionary has no elements |
keys() | Returns an Enumeration of the keys |
put(Object, Object) | Stores the specified key and value pair in the Dictionary |
remove(Object) | Removes an element from the Dictionary based on its key |
size() | Returns the number of elements stored |
The hash table data structure is very useful when searching for and manipulating data. You should use the Hashtable class if you will be storing a large amount of data in memory and then searching it. The time needed to complete a search of a hash table is decidedly less than what it takes to search a Vector. Of course, for small amounts of data, it doesn't make much difference whether you use a hash table or a linear data structure, because the overhead time is much greater than any search time would be. See the following sidebar for more information about search times in the different classes.
Hash table organization is based on keys, which are computed based on the data being stored. For example, if you want to insert a number of words into a hash table, you can base your key on the first letter of the word. When you come back later to search for a word, you can then compute the key for the item being sought. By using this key, search time is drastically reduced because the items are stored based on the value of their respective key.
The Hashtable class implements a hash table storage mechanism for storing key and value pairs. Hash tables are designed to quickly locate and retrieve stored information by using a key. Keys and values can be of any object type, but the key object's class must implement the hashCode() and equals() methods.
The sample Hashtable1 in Listing 11.10 creates a Hashtable object and stores 10 key and value pairs using the put() method. It then uses the get() method to return the value corresponding to a key entered by the user.
import java.io.DataInputStream; import java.lang.Integer; import java.lang.Math; import java.util.Random; import java.util.Hashtable; class Hashtable1 { public static void main(String args[]) throws java.io.IOException { DataInputStream dis=new DataInputStream(System.in); int numElements=10; String keys[]={"Red","Green","Blue","Cyan","Magenta", "Yellow","Black","Orange","Purple","White"}; Hashtable ht; Random randGen=new Random(); ht=new Hashtable(numElements*2); for (int i=0;i<numElements;i++) ht.put(keys[i],new Integer(Math.abs( randGen.nextInt())%numElements)); System.out.println(ht.toString()); String keyValue; System.out.println("Which key to find? "); keyValue=dis.readLine(); Integer value=(Integer)ht.get(keyValue); if (value!=null) System.out.println(keyValue+" = "+value); }
}
The output from this program looks like this:
{Cyan=4, White=0, Magenta=4, Red=5, Black=3, Green=8, Purple=3, Orange=4, Yellow=2, _Blue=6} Which key to find? Red Red = 5
In addition to the get() method, the contains() and containsKey() methods can be used to search for a particular value or key. Both return true or false depending on whether the search was successful. The contains() method must perform an exhaustive search of the table and is not as efficient as the containsKey() method, which can take advantage of the hash table's storage mechanism to find the key quickly.
Because hash tables must allocate storage for more data than actually is stored, a measurement called the load factor indicates the number of used storage spaces as a fraction of the total available storage spaces. The load factor is expressed as a value between 0 and 100 percent. Typically, the load factor should not be higher than about 50 percent for efficient retrieval of data from a hash table. When specifying the load factor in a program, use a fractional value in the range 0.0 to 1.0 to represent load factors in the range 0 to 100 percent.
Hash tables can be constructed in three different ways: by specifying the desired initial capacity and load factor; by specifying only the initial capacity; or by specifying neither the initial capacity nor the load factor. If the load factor is not specified, the Hashtable is rehashed into a larger table when it is full--otherwise, it is rehashed when it exceeds the load factor. The constructors throw an IllegalArgumentException if the initial capacity is less than or equal to zero, or if the load factor is less than or equal to zero.
The clone() method can be used to create a copy (a clone) of the Hashtable. However, it creates a shallow copy of the Hashtable, which means that the keys and values themselves are not clones. This local method overrides the inherited clone() method.
The Hashtable class extends the java.util.Dictionary class and implements the java.lang.Cloneable interface. Table 11.14 summarizes the methods of the Hashtable class.
Method | Description |
Constructors |
|
Hashtable() | Constructs an empty Hashtable |
Hashtable(int) | Constructs an empty Hashtable with the specified capacity |
Hashtable(int, float) | Constructs an empty Hashtable with the given capacity and load factor |
Methods |
|
clear() | Deletes all elements from the Hashtable |
clone() | Creates a clone of the Hashtable |
contains(Object) | Returns true if the specified object is an element of the Hashtable |
containsKey(Object) | Returns true if the Hashtable contains the specified key |
elements() | Returns an Enumeration of the Hashtable's values |
get(Object) | Returns the object associated with the specified key |
isEmpty() | Returns true if the Hashtable has no elements |
keys() | Returns an Enumeration of the keys |
put(Object, Object) | Stores the specified key and value pair in the Hashtable |
rehash() | Rehashes the contents of the table into a bigger table |
remove(Object) | Removes an element from the Hashtable based on its key |
size() | Returns the number of elements stored |
toString() | Converts the contents to a very long string |
The Properties class is what enables end-users to customize their Java program. For example, you can easily store values such as foreground colors, background colors, font defaults, and so on and then have those values available to be reloaded. This arrangement is most useful for Java applications, but you can also implement it for applets. If you have an applet that is regularly used by multiple users, you can keep a properties file on your server for each different user; the properties file is accessed each time that user loads the applet.
The Properties class is a Hashtable, which can be repeatedly stored and restored from a stream. It is used to implement persistent properties. The Properties class also allows for an unlimited level of nesting, by searching a default property list if the required property is not found. The fact that this class is an extension of the Hashtable class means that all methods available in the Hashtable class are also available in the Properties class.
The sample program Properties1 in Listing 11.11 creates two properties lists. One is the default property list and the other is the user-defined property list. When the user property list is created, the default Properties object is passed. When the user property list is searched, if the key value is not found, the default Properties list is searched.
import java.io.DataInputStream; import java.lang.Integer; import java.util.Properties; class Properties1 { public static void main(String args[]) throws java.io.IOException { int numElements=4; String defaultNames[]={"Red","Green","Blue","Purple"}; int defaultValues[]={1,2,3,4}; String userNames[]={"Red","Yellow","Orange","Blue"}; int userValues[]={100,200,300,400}; DataInputStream dis=new DataInputStream(System.in); Properties defaultProps=new Properties(); Properties userProps=new Properties(defaultProps); for (int i=0;i<numElements;i++){ defaultProps.put(defaultNames[i], Integer.toString(defaultValues[i])); userProps.put(userNames[i], Integer.toString(userValues[i])); } System.out.println("Default Properties"); defaultProps.list(System.out); System.out.println("\nUser Defined Properties"); userProps.list(System.out); String keyValue; System.out.println("\nWhich property to find? "); keyValue=dis.readLine(); System.out.println("Property '"+keyValue+"' is '"+ userProps.getProperty(keyValue)+"'"); } }
Notice that the getProperties() method is used instead of the inherited get() method. The get() method searches only the current Properties object. The getProperties() method must be used to search the default Properties list. An alternative form of the getProperties() method has a second argument: a Properties list that is to be searched instead of the default specified when the Properties object was created.
The propertyNames() method can be used to return an Enumeration, which can be used to index all the property names. This Enumeration includes the property names from the default Properties list. Likewise, the list() method, which prints the Properties list to the standard output, lists all the properties of the current Properties object and those in the default Properties object.
Properties objects can be written to and read from a stream using the save() and load() methods. In addition to the output or input stream, the save() method has an additional string argument that is written at the beginning of the stream as a header comment.
Table 11.15 summarizes the methods of the Properties class.
Variable | Description |
defaults | Default Properties list to search |
Constructors |
|
Properties() | Constructs an empty property list |
Properties(Properties) | Constructs an empty property list with the specified default |
Methods |
|
getProperty(String) | Returns a property given the key |
getProperty(String, String) | Returns a property given the specified key and default |
list(PrintStream) | Lists the properties to a stream for debugging |
load(InputStream) | Reads the properties from an InputStream |
propertyNames() | Returns an Enumeration all the keys |
save(OutputStream, String) | Writes the properties to an OutputStream |
The Observable class acts as a base class for objects you want to have observed by other objects that implement the Observer interface. An Observable object can notify its Observers whenever the Observable object is modified using the notifyObservers() method. This method accomplishes the notification by invoking the update() method of all its Observers, optionally passing a data object that is passed to notifyObservers(). Observable objects can have any number of Observers.
Table 11.16 summarizes the complete interface of the Observable class.
Method | Description |
Constructor |
|
Observable() | Constructs an instance of the Observable class. |
Methods |
|
addObserver(Observer) | Adds an Observer to the observer list |
clearChanged() | Clears an observable change |
countObservers() | Returns the number of Observers |
deleteObserver(Observer) | Deletes an Observer from the observer list |
deleteObservers() | Deletes all Observers from the observer list |
hasChanged() | Returns true if an observable change has occurred |
notifyObservers() | Notifies all Observers when an observable change has occurred |
notifyObservers(Object) | Notifies all Observers of a specific observable change |
setChanged() | Sets a flag to indicate that an observable change has occurred |
This chapter described the classes that make up the Java utilities package. This package provides complete implementations of the basic data structures and some of the most useful data types (other than the fundamental numeric types) needed by programmers. Many of the data types and data structures you will develop using Java will be based on the classes found in the utilities package. For smaller applets, many of these classes are not necessary. However, as your applets increase in complexity, you will find these classes to be very useful. In any case, this chapter has been a good starting point for understanding the utility of these important Java classes and for understanding how to use them effectively.
©Copyright, Macmillan Computer Publishing. All rights reserved.