by Paul Santa Maria
"Java," to quote its authors on the subject, "is a simple, robust, object-oriented, platform-independent, multi-threaded, dynamic, general-purpose, programming environment."
Although this rather immodest sentence was meant as a humorous reference to all of the marketing buzzwords and hoopla surrounding Java, the authors quite seriously go on to back up each of their claims. You can read the entire white paper from which this quotation was taken at Sun Microsystems' Web site at http://java.sun.com/java.sun.com/allabout.html.
After you learn the rudiments of programming in Java and you've had a chance to experiment with this new language for a while, then you, too, will share the excitement and agree with some (or all!) of the preceding claims. The intent is not to show you everything, but to give you a practical, hands-on "jump start" in learning the Java language. Read on to learn what Java is, what it is not, and how to use it effectively on your own Web pages.
The term Java can mean any of several completely different things, depending on whom you talk to, such as:
At the time of this writing (mid-1996), the Internet is still in its infancy as a medium for dynamic mass communications.
The basic technology that makes up the Internet has been around for many years. But it was the introduction of a simple graphical user interface-the Web browser-that suddenly made it so incredibly popular among millions of users worldwide.
But most Web pages now seen on the Internet have a relatively primitive, static character. Take away all the gaudy flying logos and ticker tapes, and you're usually left with one of the following:
Although forms and image maps allow a measure of user interaction, all of the main processing is done remotely on the Web server. For any frequently used site (such as www.netscape.com), this incurs a considerable load on the server. Moreover, the final result of all of the server's hard work is (you guessed it!) more static HTML, which is downloaded only to be passively displayed by your Web browser.
This kind of interaction is not the style of computing preferred by a generation of users weaned on productivity tools such as VisiCalc, Adobe PageMaker, Microsoft Word, PowerPoint, Lotus Notes, and, above all, PGA Golf. We are all used to the benefits of running our applications locally, on our very own personal computers.
Java promises an alternative model for Web content-a model much closer to the spirit of computer programs people are running on their own PCs. The crucial difference between a Java-based program and a traditional PC application is that Java programs are, by nature, network-aware and truly distributed. As creatures of the Internet, Java programs offer all of the benefits of locally executed programs: responsiveness, the capability to take advantage of local computing resources, and so on. Yet, at the same time, Java programs break the shackles of being tied to a single PC. They can suddenly take advantage of computing resources from the entire, global Internet! You'll get a taste of this awesome potential as you continue to learn about writing Java applets.
In the context of the Web, Java applets offer the following advantages:
Java syntax is very similar to C and C++. At first glance, this makes the language immediately accessible to the millions of practicing C/C++ programmers. But as you'll see in the next section, Java and C might look very much alike, but they are not identical, and sometimes apparent similarities can be misleading.
The following four tables, 42.1 through 42.4, summarize Java's
basic language constructs.
| Type | Example | Notes |
| boolean | boolean flag = false; | A Java boolean is just true or false. It cannot be cast to char or int. |
| char | char c[] = {'A','\uu42','C'}; | A Java char is a 16-bit Unicode character. You'll usually use the Java class String instead. |
| Byte | byte b = 0x7f; | 8-bit signed integer (-127 .. 127) |
| short | short s = 32767; | 16-bit signed integer (-32,768 .. 32767) |
| int | int i = 2; | 32-bit signed integer |
| long | long l = 2L; | 64-bit signed integer. Note the suffix L is required for a long (decimal) literal. |
| Float | float x = 2.0F; | 32-bit IEEE754 number. Note the suffix F is required for a float literal. |
| Double | double d = 2.0; | 64-bit IEEE754 number (15 significant digits) |
| TIP |
Java is a strongly-typed language. You must explicitly declare the "type" of every single variable that you use, and you can't arbitrarily mix or inter-convert types as easily as you can in C++ or Basic Java was deliberately engineered this way. In the long run, the use of strong typing tends to eliminate many common bugs and yields safer, more robust software products. But for novice Java programmers, the compiler's strict typing rules can be a source of frustration. The easiest way to deal with this problem is to focus on classes instead of primitive data types. By thinking at this higher level (at the class level), you'll probably need fewer primitive types and they'll be less likely to interact with one another in troublesome ways. By forcing yourself to think in terms of Java "classes," you'll save yourself some headaches, and you'll probably end up with simpler, more robust program designs, too! |
| Operator | Description |
| . | Member selection |
| [] | Array subscript |
| () | Parenthesis/Function call |
| ++, - | Auto-increment/Auto-decrement |
| *, /, % | Arithmetic: Multiply, divide, modulo |
| +, -, | Arithmetic: Add, subtract |
| <<, >>, >>> | Bitwise: Shift left, Arithmetic shift right, and Logical shift right |
| <=, <, >, >= | Equality: Less than or equal to, Less than, Greater than, Greater than or equal to |
| ==, != | Equality: Equal to, Not equal to |
| &, |, ^, ~ | Bitwise: AND, OR, Exclusive Or (XOR), and NOT |
| &&, ||, ! | Logical: AND, OR, and NOT |
| ? : | Conditional expression |
| = | Simple assignment |
| *=,/=, %=, +=, -=, &=, | Complex assignment |=, ^=, <<=, >>=,>>>= |
| TIP |
The operators in this table are arranged in order of precedence. For example, the compiler will treat the expression 2 + 2 * 2 ^ 2 as 2 + (2 * (2 ^ 2)), executing 2 XOR 2 first, 2 * the result next, and so on In your own Java code, always make liberal use of parentheses to explicitly state the order in which you want the operations in your expression to be carried out. Using parentheses instead of relying on the default precedence hierarchy will help you avoid a common source of bugs. |
| Construct | Example |
| if...then...else | if (i >= salesGoal) { ... } |
| for | for (i = 0; i < maxItems; i++) {...} |
| while | while (i < salesGoal) { ... } |
| do...while | do { ... } while (i < salesGoal); |
| switch (...) case | switch (i) { case 1: ... break; } |
| break | while (i < salesGoal) { if (I==10) break;...} |
| continue | while (i < salesGoal) { if (I==10) continue; ... } |
| labelled break | while (i < salesGoal) { if (I==10) break my_label;...} |
| Comment style | Format | Notes |
| C comments | /* ... */ | Can span multiple lines |
| C++ comments | // ... | Comment stops at the end of the line: less prone to error |
| Javadoc comments | /** ... */ | Appropriate for header comments: lets you auto-generate program documentation |
| ON THE WEB |
http://java.sun.com/newdocs.html This site will give you all the details on Sun's official Java documentation, including reference manuals and language tutorials |
Although operators and data types are obviously very important in Java, classes are where the real action is.
In his text Object-Oriented Modeling and Design, James Rumbaugh defined a "class" as describing "...a group of objects with similar properties (attributes), common behavior (operations), common relationships to other objects, and common semantics." An object, on the other hand, is simply an instance of a class, dynamically created by the program during runtime. In other words, classes are definitions; objects are the real thing.
In Java, everything is a class. Unlike C and C++, Java has no structs and no free subprograms (a "free subprogram" is a subroutine or function that exists independently of a class). Most of the power of C++ stems from the simple notion of extending C's basic struct into the notion of a C++ class, which encapsulates both state (program data) and methods (a program's algorithms) into a single logical entity. Java completes this transition by recognizing that, after you have the power of classes, structs become irrelevant!
Java was designed to be both a simpler and a safer language than C++. Many features of C++ such as multiple inheritance were deliberately left out of Java because Java's authors felt that they could make their new language less complex and make Java programs less prone to common C++ programming and design errors.
Table 42.5 gives a brief overview of Java classes in comparison
to C++.
| Construct | ||
| Class | ||
| Single Inheritance | ||
| Multiple Inheritance | ||
| Constructors | ||
| Destructors | ||
| Templates | ||
| Packages | ||
| Interfaces | ||
| Packages | ||
| Interfaces |
Basically, traditional methods force you to "think like the machine" and break things down into modules, variables, parameters, and the like. Object-oriented methods allow you to think at a much higher level-in terms of objects, their behavior, and how they relate to one another.
The interesting thing is that Java (unlike, for example, C++) forces you to think in an object-oriented style. When you work with Java, you will probably find yourself spending most of your development time figuring out what "objects" your program needs, and browsing to see whether your library already has existing (canned) classes that you can inherit from, thus reusing existing code with little or no additional effort on your part!
This is a marked contrast from traditional programming styles in languages such as FORTRAN, C, or Pascal, where the bulk of your effort goes into "decomposing" a problem into modules, then creating algorithms the modules use to process data. In many subtle (and many not so subtle!) ways, Java almost forces the programmer to abandon old procedural habits in favor of a more object-oriented perspective.
We will return to this discussion later. For now, let's get on with the fun stuff-coding and running our very first Java applet!
At this writing, Java is so new that there are relatively few decent tools for creating Java applications. Symantec's Caf Lite, Borland C++ 5.0, and Microsoft Visual J++ come immediately to mind as excellent, GUI-based development tools.
But for purposes of our discussion, let's stick with the original Java tools that are freely available from the creators of Java, Sun Microsystems. These tools are the following:
You can download a free copy of the Java JDK at http://www.javasoft.com/.
| TIP |
Java requires a 32-bit operating system and support for long, case-sensitive file names. In the PC arena, Java supports only Windows 95 or Windows NT |
First, create a program source file, as shown in Listing 42.1.
Listing 42.1 HelloWorld.java-First Java Applet
HelloWorld.java
/**
* HelloWorld: Rudimentary Java applet
*
* @version 1.0, DATE: 8.11.96
* @author Paul Santa Maria
*/
import java.applet.*;
import java.awt.*; // User Interface components
//
// MyCanvas: graphics area to draw "Hello World!"
//
class MyCanvas extends Canvas
{
public void paint (Graphics g)
{
g.drawString ("Hello from Java!", 0, 50);
}
}
//
// HelloWorld: sample program "main"
//
public class HelloWorld extends Applet
{
private MyCanvas canvas;
// init: applet initialization code goes here
public void init ()
{
setLayout (new BorderLayout());
canvas = new MyCanvas ();
add ("Center", canvas);
}
// handleEvent: give us a way to gracefully exit the program
public boolean handleEvent (Event evt)
{ if (evt.id == Event.WINDOW_DESTROY)
System.exit (0);
return false;
}
}
| TIP |
You must use a text editor that supports long, case-sensitive file names The versions of Notepad, WordPad, and Edit that come with Windows 95 work fine. Word for Office 4.0 also works. Earlier versions of Word for 16-bit Windows do not work. Your Java source file must have the same name as the main class. That is, you must name this file HelloWorld.java, which corresponds to the main class HelloWorld. Furthermore, the capitalization must also match exactly. If your class is named HelloWorld but your source file is named Helloworld.java (note the lowercase "w"), the program will not compile! This close relationship between class names and program source files is another of many reasons why it is not practical to port Java development tools to DOS or Windows 3.1 (neither of which supports long or mixed-case file names)! Complete source code for each of these programs is included on the accompanying CD-ROM. |
Basically, all you're doing here is using the canned functionality that's already built in to the Java class applet, and tweaking it slightly for your purposes by using inheritance (for example, class HelloWorld extends applet)-and that's the entire program!
We're also doing the same thing with MyCanvas. We just inherit from some pre-existing class that does almost what we want, tweak it slightly with some new functionality, then shamelessly use the whole thing as though we had done all the work ourselves.
You may recall from our earlier discussion that Java applets are somewhat different beasts than stand-alone Java applications. Applets are specifically designed to run from within the context of an HTML Web page. So let's write a simple Web page to test our new applet (see Listing 42.2).
Listing 42.2 Example.html-HTML Web Page that Calls the Applet
<HTML> <TITLE>Hello World!</TITLE> <BODY> <H1>HelloWorld: a first Java Applet:</H1> <APPLET CODE="HelloWorld.class" WIDTH=120 HEIGHT=120> If you can read this, then your browser is not set up for Java... </APPLET> <HR> <A HREF="HelloWorld.java">The source.</A> </BODY> </HTML>
| TIP |
Your HTML source file is not subject to the same strict rules about long file names and case-sensitivity as your Java source files |
Finally, let's write a convenient batch file to compile and execute our program (see Listing 42.3).
Listing 42.3 Demo.bat-Windows Batch File for Compiling and Running the Applet
demo.bat: @rem Compile and execute "Hello world" sample program javac HelloWorld.java appletviewer example.html
| TIP |
This method assumes that you have SunSoft's text-oriented javac compiler and appletviewer installed on your system, and that you have your Windows 95 PATH and CLASSPATH environment variables set correctly Following is a sample from an Autoexec.bat file that sets these two variables: PATH=c:\windows;c:\windows\command;c:\java\bin Note that Setup.exe for the Windows 95 or Windows NT JDK should automatically install these definitions in your Autoexec.bat for you. |
Figure 42.1 shows the result of our very first program, as seen through SunSoft's appletviewer.
Figure 42.1 : The JDK provides a simple appletviewer for previewing and testing your applets.
Although you'll find it convenient to use appletviewer (or your Java compiler's IDE) as you first develop your applets, you should always test them on a real Web browser before you actually publish your applets to the outside world.
Let's now run the same Java program from Netscape Navigator (see Figure 42.2).
Figure 42.2 : You can also view your applet through any Java-enabled Web browser.
You may have noticed that none of your HTML (HelloWorld: a first Java Applet, and so on) showed up when you looked at it through appletviewer. This is because it is not a full-fledged Web browser. appletviewer is intended only for viewing applets! Nevertheless, as you start coding and testing, you will undoubtedly find appletviewer a more convenient test bed until you get your applets completely up and running.
You may have also noticed that when you viewed your applet under Netscape, the applet looked just like the surrounding HTML: just plain, old boring text. Fear not! We're going to get into graphics in our next, only slightly more difficult, example (a Mandelbrot Set program).
Even with an applet as simple as this, there are a number of things that could go wrong. Let's take a look at some common "gotcha's":
PATH=c:\windows;c:\windows\command;c:\java\bin CLASSPATH=c:\java\lib;.
Sometimes, programmers become impatient with the analysis and design phases of software development, preferring to jump right in and start coding. This attitude is often justified. For small programs like those found in textbooks, it makes little sense to go to all the trouble of requirements, analysis, design, integration, test, and so on. For a small program, you just fire up your compiler, whip up some code, and voil!-you're done!
But life just isn't like that for larger, more complex, or, dare we say it, mission-critical applications. For projects like those, we all need to sit down and plan ahead! One of the beautiful things about Java is that it's expressly designed to scale up to large, mission-critical projects. In all honesty, you'll be doing yourself a tremendous favor if you get into the habit of thinking about design issues now, when you're starting out on the simple programs. As a consequence, it will be second-nature for you later when you need it on your large-scale projects.
Let's look at a class hierarchy diagram for our simple HelloWorld program (see Figure 42.3).
You may recognize the two boxes HelloWorld and MyCanvas. These correspond to the two classes, HelloWorld and MyCanvas, that we used in our program.
The lines connecting HelloWorld with Applet and MyCanvas with Canvas illustrate an inheritance relationship. This is often called an is-a relationship; class HelloWorld is-an Applet; MyCanvas is-a Canvas. Applet and Canvas are called parent classes; MyCanvas and HelloWorld are called child or sub- classes.
The boxes HelloWorld and MyCanvas also list each class's methods. You may want to avoid cluttering your diagram by listing only the few most important methods. In this diagram, we chose to list all the methods that were overridden from the base class: init(), handleEvent(), and paint(). We also showed the constructor method MyCanvas().
The line that connects HelloWorld and MyCanvas is a use, or has-a relationship. This means that class HelloWorld (which is a kind of applet) makes use of MyCanvas (which is, in turn, a subclass of Canvas).
Let's continue and try some Java graphics.
| TIP |
If you're interested in reading more on the subject, two excellent books are the following |
One of the nifty things about using Java is that all of the principal documentation is completely online in Web format. The Java API manual is automatically on your hard disk when you install the JDK (see Figure 42.4). The latest-and-greatest version of the manual is also available directly on the Internet at the following addresses:
http://www.javasoft.com/doc/api_documentation.html
http://www.javasoft.com/products/JDK/CurrentRelease/api/
Figure 42.4 : The handy, fully Web-based Java API manual is an indispensable programming resource.
Fractals are an area of mathematics dealing with fractional dimensions. You probably recall from high school geometry that a line is a one-dimensional object, a square is two-dimensional, a cube is three-dimensional, and so forth. This all has to do with the traditional, comfortable world of Euclidean geometry, where two parallel lines never meet and the sum of the three angles in a triangle will always equal 180¡. Fractals deal with a whole other spectrum of dimensions, one in which a line might have a dimension not of 1, but of, say, 1.3756!
Around the turn of the century, mathematicians and philosophers began conceiving strange, abstract worlds where the common sense rules governing the Euclidean Universe were no longer true. The notion of fractional dimensions was conceived at that time, but it wasn't until well into the age of computers nearly 60 years later that it became practical to investigate the subject. The man who first applied computers to the subject, and who coined the term fractal, was Dr. Benoit Mandelbrot, of the IBM Research Labs.
Although fractals have proven themselves to be of practical value in everything from CGI special effects in Hollywood movies to fabulously efficient digital image compression techniques, we're interested in one kind of fractal-a Mandelbrot Set-for the following reasons:
If you want to learn more about fractals, check out any of the following:
Chaos: Making a New Science, by James Gleick (Viking Penguin, 1987)
The Spanky Fractal Database, http://spanky.triumf.ca/www/whats-new.html
Beauty of Fractals: Images of Complex Dynamical Systems, by H.O. Peitgen & P.H. Richter (Springer Verlag, 1986)
Figure 42.5 shows the preliminary design for your Mandelbrot program's class structure.
Figure 42.5 : Mandelbrot Set Class diagram.
You may have noticed that this class diagram shown in Figure 42.5 is almost identical to Figure 42.3, the class diagram for our HelloWorld example. It consists only of an applet (an execution context for our program to exist in), a canvas (to render graphics to), and one new class (a color map), so that we draw in more than just black and white. Note, too, that we developed our class diagram (however rudimentary) before we started coding.
Following is our sample Demo.bat (see Listing 42.4), Example.html (see Listing 42.5), and Mandel1.java (see Listing 42.6)source files. These, too, are very similar to the ones in our earlier HelloWorld applet.
Listing 42.4 Demo.bat-Windows Batch File for Compiling and Running the Mandelbrot Set
@rem Compile and execute sample program javac Mandel1.java appletviewer example.html
Listing 42.5 Example.html-HTML Web Page that Calls the Mandelbrot Applet
example.html <HTML> <TITLE>Mandelbrot Set</TITLE> <BODY> <APPLET CODE="Mandel1.class" WIDTH=425 HEIGHT=425> If you can read this, then your browser is not set up for Java... </APPLET> <HR> <A HREF="Mandel1.java">The source.</A> </BODY> </HTML>
Listing 42.6 Mandel1.java-First Version of Mandelbrot Program
/**
* Mandel1: Plots Mandelbrot set and displays in Web Browser
*
* @version 1.0, DATE: 8.17.96
* @author Paul Santa Maria
*/
import java.applet.*;
import java.awt.*; // User Interface components
//
// MandelPlot: Implements Mandelbrot plot
//
class MandelPlot extends Canvas
{
private int maxcol = 399, maxrow = 399, max_colors = 8,
max_iterations = 512, max_size = 4;
private Color cmap[];
private void plot (Graphics g, int x, int y, int color_index)
{
g.setColor (cmap[color_index]);
g.drawLine (x,y, x,y);
}
// MandelPlot display image constructor
public MandelPlot ()
{
cmap = new Color[max_colors];
cmap[0] = Color.black;
cmap[1] = Color.red;
cmap[2] = Color.green;
cmap[3] = Color.blue;
cmap[4] = Color.cyan;
cmap[5] = Color.magenta;
cmap[6] = Color.yellow;
cmap[7] = Color.white;
}
// paint: actually draws the Mandelbrot set
public void paint (Graphics g)
{
float Q[] = new float[400];
double Pmax = 1.75,Pmin = -1.75, Qmax = 1.5, Qmin = -1.5,
P, deltaP, deltaQ, X, Y, Xsquare, Ysquare;
int color, row, col;
deltaP = (Pmax - Pmin)/(double)(maxcol - 1);
deltaQ = (Qmax - Qmin)/(double)(maxrow - 1);
for (row=0; row<=maxrow; row++) {
Q[row] = (float)(Qmin + row*deltaQ);
}
for (col=0; col<=maxcol; col++) {
P = Pmin + col*deltaP;
for (row=0; row<=maxrow; row++) {
X = Y = 0.0;
color = 0;
for (color=0; color<max_iterations; color++) {
Xsquare = X*X;
Ysquare = Y*Y;
if ((Xsquare+Ysquare) > max_size)
break;
Y = 2*X*Y+Q[row];
X = Xsquare-Ysquare+P;
}
plot (g, col, row, (int)(color % max_colors));
}
}
}
}
//
// Mandel1: sample program "main"
//
public class Mandel1 extends Applet
{
private MandelPlot canvas;
// Mandel: Frame constructor
public void init ()
{
setLayout (new BorderLayout());
canvas = new MandelPlot ();
add ("Center", canvas);
}
// handleEvent: give us a way to gracefully exit the program
public boolean handleEvent (Event evt)
{ if (evt.id == Event.WINDOW_DESTROY)
System.exit (0);
return false;
}
}
Run Demo.bat to compile and link this program, and you should see the resulting image, as shown in Figure 42.6.
Believe it or not, the structural similarity between our first two example programs (HelloWorld and Mandel1) is more than just coincidence. It gives us an important clue as to how tools such as Microsoft's AppWizard or Borland's AppExpert work and, more generally, why object-oriented programming has become so popular.
When you design your own programs, you need to figure out what kinds of things your program will be using, and then encapsulate the essential behavior of each into its own class.
Library designers do the same kind of thing, only at a higher level. What the designers of Java have done is they figured out what kinds of building blocks application developers might typically need, then assembled these into a general-purpose application framework. Ideally, an application framework lets you write entire applications almost on demand, with only minimal tailoring.
In Java, the application framework is called the Abstract Windows Kit (AWT). AWT is really very similar in spirit to Windows programming frameworks such as Microsoft's Foundation Classes (MFC) and Borland's Object Windows Library (OWL).
Both Microsoft and Borland bundle their respective application frameworks with their compilers, and supply sophisticated tools that make it possible to generate tailored programs with a simple point-and-click graphical interface. There are already tools from Symantec, Rogue Wave, BlueStone, IBM, and SunSoft that match, or surpass this level of functionality for Java programmers.
When you run this program, the first thing you may notice is that it's slow. It can take upwards of five to 10 minutes to plot on a Pentium-class machine (never mind the fact that the same code could have taken overnight on an old AT-class machine!).
Table 42.6 is a comparison of the same basic Mandelbrot program,
run on exactly the same PC, compiled in different ways.
| Platform | ||
| Native MS-Windows (Non-Java) | ||
| Java App (Windows 95) | ||
| Java Applet (Windows 95/Applet Viewer) | ||
| Java Applet (Windows 95/Netscape) |
So do we have to live with this? Or can we somehow improve our program's performance? The answer to the second question is, "Yes: We can speed things up. Perhaps by a lot!"
Although it is expected that increasingly sophisticated development tools and just-in-time runtime optimizers will soon allow Java programs running within a Java virtual machine to run almost as fast as native executables, we cannot afford to wait for them. Fortunately, there is a great deal we can do ourselves-today-in terms of manually tuning performance.
But to optimize our code, we first need to understand exactly what it's doing, and how long it is taking each step of the way. We'll address this issue in the following two ways:
How long does MandelPlot take to run on your PC? You can pull out a stopwatch and time it yourself, but that could get really boring really fast. Why not write a program to do it? Let's call this hypothetical program StopWatch. And let's make it general enough that we can reuse it in future programs that we might want to benchmark.
To use a stopwatch, you click once to start timing, then click it again when you want to stop. You can click yet again if you want to resume timing. The watch always shows you the elapsed time since you started. Additionally, you can click another button to reset back to 0:00.
The concept is very simple, but coding it can be tricky. The problem is that it can be difficult to keep track of which operation is legal when. What happens if you resume before you hit start? Or pause after you've already stopped? Unless you keep careful track of all these subtle rules, it's all too easy to write a buggy program.
We can quickly and easily capture the semantics of a class such as StopWatch with a State Transition diagram, as shown in Figure 42.7.
The circles represent each state our class can be in at any given moment: stopped, started, or paused. The lines show all of the legal transitions from one state into another.
Compare this State Transition diagram with the code for the class methods start(), stop(), pause(), and resume() in the Listing 42.7. As you can see, the source code practically falls right out of the state diagram! With any class that exhibits significant event-ordered behavior, sketching out a State Transition diagram can really simplify your work by suggesting a straightforward, simple design.
Listing 42.7 StopWatch.java-Implements Timing Functions
import java.util.*; // Date, etc.
/**
* StopWatch: Implements timing functions<p>
*
* <pre>SAMPLE USAGE:
* StopWatch t = new StopWatch ();
* t.start ();
* ...
* t.stop ();
* System.out.println ("elapsed time: "
* + t.getHH () + ", " + t.getMM () + ", " + t.getSS () );</pre>
*
* @version 1.1, DATE: 8.17.96
* @author Paul Santa Maria
*/
public class StopWatch
{
public final static int STOPPED = 0;
public final static int RUNNING = 1;
public final static int PAUSED = 2;
private int hh1, mm1, ss1;
private int hh2, mm2, ss2;
private int ehh, emm, ess;
private int current_state;
/*
* store t1
*/
private final void getStartTime ()
{
Date d = new Date ();
hh1 = d.getHours ();
mm1 = d.getMinutes ();
ss1 = d.getSeconds ();
}
/*
* store t2
*/
private final void getCurrentTime ()
{
Date d = new Date ();
hh2 = d.getHours ();
mm2 = d.getMinutes ();
ss2 = d.getSeconds ();
}
/*
* compute (HH:MM:SS) ((t2-t1)+elapsed)
*/
private final boolean computeElapsed ()
{
int hh, mm, ss;
//Check if we've already computed elapsed time
if (current_state == STOPPED || current_state == PAUSED) {
return true;
}
getCurrentTime ();
// Compute seconds
if (ss2 < ss1) {
ess += (60 + ss2) - ss1;
mm2 -= 1;
}
else
ess += ss2 - ss1;
// Compute minutes
if (mm2 < mm1) {
emm += (60 + mm2) - mm1;
hh2 -= 1;
}
else
emm += mm2 - mm1;
// Compute hours
ehh = hh2 - hh1;
// Now translate straight decimal to 60:60
if (ess >= 60) {
emm += 1;
ess -= 60;
}
if (emm >= 60) {
ehh += 1;
emm -= 60;
}
// start incrementing again from current time
getStartTime ();
// Done!
return true;
}
/**
* public constructor
*/
public StopWatch ()
{
reset ();
current_state = STOPPED;
}
/**
* Start incrementing time
*/
public void start ()
{
if (current_state == STOPPED) {
reset ();
current_state = RUNNING;
}
}
/**
* Stop incrementing time
*/
public void stop ()
{
if (current_state == RUNNING) {
getCurrentTime ();
computeElapsed ();
}
current_state = STOPPED;
}
/**
* Clears elapsed time, re-initializes start time
*/
public void reset ()
{
ehh = emm = ess = 0;
getStartTime ();
}
/**
* Stop incrementing time (until next start)
*/
public void pause ()
{
if (current_state == RUNNING) {
getCurrentTime ();
computeElapsed ();
current_state = PAUSED;
}
}
/**
* Resume incrementing time (until next pause)
*/
public void resume ()
{
if (current_state == PAUSED) {
getStartTime ();
current_state = RUNNING;
}
}
/**
* Returns elapsed HH
*/
public int getHH ()
{
if (computeElapsed ())
return ehh;
else
return 0;
}
/**
* Returns elapsed MM
*/
public int getMM ()
{
if (computeElapsed ())
return emm;
else
return 0;
}
/**
* Returns elapsed SS
*/
public int getSS ()
{
if (computeElapsed ())
return ess;
else
return 0;
}
/**
* Return stopwatch state
*/
public int getState ()
{
return current_state;
}
}
Being good programmers, we always create a test driver for any substantial new piece of software. Right?
Because it's slightly simpler, let's write TestStopWatch as a stand-alone application instead of a Java applet. We'll have more to say about how easy it is to switch back and forth between Java applets and stand-alone Java programs (and why one would ever want to do so!) in a few moments.
Basically, all we want to do is check out all the public interfaces to our new StopWatch class, see the results, and make sure everything works as we expect it to.
Complete source code for TestStopWatch.java is shown in Listing 42.8.
Listing 42.8 TestStopWatch.java-A Test Harness for StopWatch
import java.awt.*; // GUI components
/**
* TestStopWatch: Test harness for our StopWatch utility class
*
* @version 1.1, DATE: 8.18.96
* @author Paul Santa Maria
*/
public class TestStopWatch extends Frame
implements Runnable
{
public TestStopWatch ()
{
// Set Window title (would happen automatically if this were an
// applet instead of a frame)
setTitle ("TestStopWatch");
// Create a "panel" to hold our clock time
Panel p1 = new Panel ();
p1.setLayout (new FlowLayout ());
p1.add (new Label ("Elapsed HH:MM:SS "));
timeHH = new TextField ("00", 3);
p1.add (timeHH);
timeMM = new TextField ("00", 3);
p1.add (timeMM);
timeSS = new TextField ("00", 3);
p1.add (timeSS);
add ("North", p1);
// Now create another "panel" to contain our pushbuttons
Panel p2 = new Panel ();
p2.setLayout (new FlowLayout ());
p2.add (new Button ("Start"));
p2.add (new Button ("Stop"));
p2.add (new Button ("Pause"));
p2.add (new Button ("Resume"));
p2.add (new Button ("Reset"));
p2.add (new Button ("Quit"));
add ("South", p2);
// Create a sample StopWatch
sw = new StopWatch ();
// Finally, create a timer thread
runner = new Thread (this);
runner.start ();
}
/*
* handleEvent: process any window events (such as "close window")
*/
public boolean handleEvent (Event evt)
{
if (evt.id == Event.WINDOW_DESTROY)
do_quit ();
return super.handleEvent (evt);
}
/*
* action: process any button clicks
*/
public boolean action (Event evt, Object arg)
{
if (arg.equals ("Start")) {
System.out.println ("Start");
sw.start ();
}
else if (arg.equals ("Stop")) {
System.out.println ("Stop");
sw.stop ();
}
else if (arg.equals ("Pause")) {
System.out.println ("Pause");
sw.pause ();
}
else if (arg.equals ("Resume")) {
System.out.println ("Resume");
sw.resume ();
}
else if (arg.equals ("Reset")) {
System.out.println ("Reset");
sw.reset ();
}
else if (arg.equals ("Quit")) {
System.out.println ("Quit");
do_quit ();
}
else {
System.out.println ("UNKNOWN EVENT");
return false;
}
// "true" means we handled this event.
// We shouldn't have any events besides button-pushes.
return true;
}
/*
* "private" class data
*/
private StopWatch sw;
private TextField timeHH, timeMM, timeSS;
private Thread runner;
/*
* do_quit: exit (somewhat gracelessly, but very conveniently!
*/
private void do_quit ()
{
// Terminate thread
runner.stop ();
// Abort the program & return to host OS
System.exit (0);
}
/*
* main
*/
public static void main (String[] args)
{
// Note: all this would happen automatically if
// this were an applet instead of a frame
Frame f = new TestStopWatch ();
f.resize (350, 100);
f.show ();
}
/*
* run: implement asynchronous timing loop
*/
public void run ()
{
// The AWT interface for run () REQUIRES that we provide
// an exception handler like this try .. catch
try {
// Loop forever
for ( ;; ) {
// Note strange syntax for converting int to String
timeHH.setText ( "" + sw.getHH () );
timeMM.setText ( "" + sw.getMM () );
timeSS.setText ( "" + sw.getSS () );
// Wait 1000 ms (1 second) for continuing loop
Thread.sleep (1000);
}
}
catch (InterruptedException e) {
}
}
}
Figure 42.8 shows what our TestStopWatch driver program looks like.
Figure 42.8 : It's always a good idea to write a test driver for any general-purpose class.
| TIP |
Debugging Java applets can present special challenges. When C++ or Basic programs fail, they usually GPF, dump core, or do something equally exciting. However, when an applet fails, it fails quietly If your applet doesn't seem to be doing anything, you could be facing one of two problems: If you suspect the problem is occurring inside your applet, you can step through your Java debugger or embed temporary printf statements (either System.out.println () or g.drawText ()) at strategic points in your source code. |
Although TestStopWatch is basically just a "quick hack" to test our StopWatch class, there are several interesting aspects of this program that bear closer scrutiny.
Compiling and Running a Java Application TestStopWatch was our first exposure to a stand-alone Java application versus a Java applet.
You may notice that although we compiled the same way (javac myProgram.java), we ran it using Java instead of appletviewer or a Web browser. Listing 42.9 shows a new Demo.bat for compiling TestStopWatch.
Listing 42.9 Demo.bat-Compile and Run TestStopWatch Program
javac StopWatch.java javac TestStopWatch.java java TestStopWatch
Inheriting Main Class from Frame Versus Applet Whereas Java applets must inherit from base class Applet, stand-alone Java applications usually inherit from base class Frame. Here is a sample comparison between an applet's base class (see Listing 42.10) and an application's base class (see Listing 42.11):
Listing 42.10 HelloWorld.java-Example Snippet from HelloWorld Applet
// This is how we typically set up the main class in a Java Applet:
public class HelloWorldApplet extends Applet
{
public void init ()
{
setLayout (new BorderLayout ());
...
Listing 42.11 AnyProgram.java-Example Snippet from Hypothetical Java Stand-Alone Application
// This is how we typically set up the main class in a Java Application:
public class HelloWorldApplication extends Frame
{
public static void main (String[] args)
{
Frame f = new myProgramFrame ();
f.resize (100, 100);
f.show ();
...
main() Method The main class in any stand-alone Java application must have a method called public static void main (String[] args).
If you forget to do this (or if you accidentally mistype any part of its declaration), your Java program will fail to run.
Some Basic Java GUI Controls TestStopWatch introduces us to our first GUI objects (known as widgets to X Windows programmers or controls to Windows programmers): pushbuttons, text edit controls, and text Labels.
Unlike Windows, where you typically declare controls in a separate resource file, Java lets you simply create them and use them on the fly. This is shown in Listing 42.12.
Listing 42.12 AnyProgram.java-Example Snippet Showing How to Arrange GUI Elements on a Java Panel
// Hey, kids! Let's make some pushbuttons!
Panel p = new Panel ();
p.setLayout (new FlowLayout ());
p.add (new Button ("Start"));
p.add (new Button ("Quit"));
add ("South", p);
Panel Versus Canvas: Automatic Layout Management in Java X Windows programmers are accustomed to relying on so-called container widgets to manage the layouts of their various GUI controls as they appear to the end-user. Windows programmers, on the other hand, must usually hard-code specific x- and y-coordinates for the height, width, and position for their controls.
Java more or less parallels the X Windows model by providing containers to manage the nitty-gritty details of positioning GUI controls for you.
Our TestStopWatch program uses two panels: p1 manages the Elapsed HH:MM:SS data on the top of our frame; p2 manages all of the pushbuttons on a single row at the bottom.
Our Frame (TestStopWatch) contains the panels. The panels, in turn, contain our GUI controls.
Automatic Event Management in Java You were probably relieved to notice that event management was reasonably straightforward: you just declared the object and checked for any interesting events in your handleEvent() method!
Introduction to Java Threads TestStopWatch also introduced us to Java's multithreading capabilities.
If TestStopWatch were written as a traditional, single-threaded C program, we would probably just fall into a simple do...while loop. We can't do that in Java, however, because if the program were trapped in a loop, it would never get a chance to handle any button-press events from the user.
If, on the other hand, TestStopWatch were written as a Windows or a Motif program, we would probably set up some kind of timer that would periodically pass some special timer event into our main event loop. Java neither has such a timer type nor a main event loop that we need to slavishly adhere to.
Instead, Java offers us what's arguably a much simpler, cleaner solution-we can just create a thread object that will be its own simple, self-contained do...while loop. Because it will run in parallel to the main program, we won't lock ourselves out of fielding any important user events. Listing 42.13 shows how it works.
Listing 42.13 TestStopWatch.java-Example Snippets Showing How to Implement a Timer Using Java Threads
// Declare that our class will be using threads:
public class TestStopWatch extends Frame
implements Runnable
// Store the Thread object as private class data:
private StopWatch sw;
private TextField timeHH, timeMM, timeSS;
private Thread runner;
// Create the thread in the main classes constructor:
public TestStopWatch () {
runner = new Thread (this);
runner.start ();
// Destroy the thread when you quit the program:
private void do_quit () {
runner.stop ();
// Put your own code in a customized run() method
public void run () {
try {
// Loop forever
for ( ;; ) {
...
}
}
catch (InterruptedException e) {}
}
Converting Java Types to "String" As we mentioned earlier, Java is a strongly typed language. Our StopWatch class keeps track of time (hours, minutes, and seconds) with integers (type int).
But we need to convert this "time" data from int to String before we can display it. How do we do this? It's easy! Listing 42.14 shows one way.
Listing 42.14 TestStopWatch.java-Example Snippet Showing One Method of Converting a Java Primitive Type into a Java String
for ( ;; ) {
timeHH.setText ( "" + sw.getHH () );
timeMM.setText ( "" + sw.getMM () );
timeSS.setText ( "" + sw.getSS () );
The magic here is that whenever you concatenate (using the + operator) a String with some value that isn't a String, Java will automatically convert the result into a String.
Here's a convenient way to put simple, C-like printf statements into Java applications:
System.out.println ("Myvalue: " + myInt)
As you've seen from our TestStopWatch example, it's often easier to write and work with a stand-alone Java application than it is a Java applet. You don't have to deal with the hassle of setting up an HTML page, nor do you have the extra overhead of running from a Web page. More significantly, it's easier to debug your program using printf() trace statements (excuse me, System.out.println() trace statements!) from an application. Finally (and this brings us back full-circle to the topic we started with), you must run in standard application mode if you want to profile your application.
Fortunately, going back and forth between Java applications and Java applets is not at all difficult. After you have a working Java application, just do the following to convert it into an applet:
Now that we've gone through all this hard work to implement a
StopWatch function, let's use it to get a rough benchmark
of our Mandelbrot program. Let's also use our newfound knowledge
about inter-converting Java applications to turn the Mandelbrot
program from an applet into an application-at least long enough
to profile and optimize it. Finally, let's also run the profiler
and get detailed statistical data.
| TIP |
Neither our StopWatch nor the Java profiler seriously affects runtime performance. The Heisenberg Uncertainty Principle does not apply here-you can safely measure our program without altering its behavior |
Let's copy our baseline code into a new directory and make the following changes:
Listing 42.15 Demo.bat-Windows Batch File for Compiling StopWatch, Mandelbrot Set, and Running with Profiling ON
@rem Compile and execute sample program javac StopWatch.java javac Mandel2.java java -prof Mandel2
Listing 42.16 Mandel2.java-Change Applet's init() Method into a Constructor Method
// Mandel: Frame constructor
public Mandel2 () {
setTitle ("Mandelbrot Set: Version 2");
canvas = new MandelPlot ();
add ("Center", canvas);
}
Listing 42.17 Mandel2.java-main() Method: Required for Any Stand-Alone Java Application
// main: create frame, display Mandelbrot set
public static void main (String args[])
Frame f = new Mandel2 ();
f.resize (400, 400);
f.show ();
}
Listing 42.18 Mandel2.java-Add StopWatch to the Mandelbrot Set Program to Show Elapsed Time
// paint: actually draws the Mandelbrot set
public void paint (Graphics g)
...
StopWatch timer = new StopWatch ();
timer.start ();
...
timer.stop ();
g.setColor (Color.black);
g.drawString ("hh: " + timer.getHH ()
+ ", mm: " + timer.getMM ()
+ ", ss: " + timer.getSS (),
0, 16);
...
Figure 42.9 shows the result of our revised Mandelbrot set, with
timing information printed out in the upper-left corner (2 minutes
and 54 seconds).
Figure 42.9 : The Mandelbrot Set with timing information (2:54) printed out.
| CAUTION |
Because we put our plot Mandelbrot set code inside of the paint() method, the image will be recomputed and redrawn anytime anything happens to the canvas area. For this reason, you might want to turn off screensavers and not open or move windows over the canvas area when you run this program. There are several advanced techniques that can get around this problem, such as using buffered images. A complete discussion of these techniques-and their relative pros and cons-is outside the scope of this chapter |
As we can see from StopWatch, it took 2 minutes, 54 seconds to
plot the Mandelbrot Set.
| TIP |
It is worth noting that this Java stand-alone application runs nearly twice as fast as the same code run as an applet from inside the Netscape 2.0 browser. This will become less of a problem as more Web browsers incorporate Just In Time compilers, which can make Java applications run nearly as fast as native executables. But speed and performance issues are clearly something to keep in mind if you're planning any especially ambitious Java applets |
Because we used the -prof command-line switch, Java created a java.prof execution profile in our working directory. Listing 42.19 shows a few snippets from the execution profile of Mandel2.java.
Listing 42.19 java.prof-Execution Profile of Mandel2 Program
# count callee caller time 1 java/awt/Component.getBackground()Ljava/awt/Color; java/awt/Component.getBackground()Ljava/awt/Color; 0 3 java/awt/Component.getFont()Ljava/awt/Font; java/awt/Component.getFont()Ljava/awt/Font; 0 3 java/awt/Component.postEvent(Ljava/awt/Event;)Z java/awt/Component.postEvent(Ljava/awt/Event;)Z 0 3 java/lang/Object.<init>()V java/lang/String.<init>(Ljava/lang/ StringBuffer;)V 0 92 java/lang/Object.<init>()V java/lang/String.<init>([C)V 0 ... 160000 sun/awt/win32/Win32Graphics.drawLine(IIII)V MandelPlot.plot(Ljava/awt/Graphics;III)V 18389 160000 sun/awt/win32/Win32Graphics.setColor(Ljava/awt/Color;)V MandelPlot.plot(Ljava/awt/Graphics;III)V 3150 11604 sun/awt/win32/Win32Graphics.pSetForeground(Ljava/awt/Color;)V sun/awt/win32/Win32Graphics.setColor(Ljava/awt/Color;)V 2486 160000 MandelPlot.plot(Ljava/awt/Graphics;III)V MandelPlot.paint(Ljava/awt/Graphics;)V 24341 1 MandelPlot.paint(Ljava/awt/Graphics;)V sun/awt/win32/MComponentPeer.paint(Ljava/awt/Graphics;)V 174850 1 sun/awt/win32/MComponentPeer.handleExpose(IIII)V ?.? 174896 ...
As you can see, the raw data generated by the profiler is not exactly user-friendly. Nevertheless, it's a useful tool and the information it yields is important for us to understand.
The profiler keeps track of every Java method that's called. It logs the following into java.prof:
We'll analyze this raw data to learn the following:
Table 42.7 shows the most time-consuming routines from java.prof,
sorted by time. These, clearly, are the most likely candidates
for optimization.
| # count | callee | caller | time |
| 11604 | pSetForeground | setColor | 2486 |
| 160000 | setColor | plot | 3150 |
| 160000 | drawLine | plot | 18389 |
| 160000 | plot | paint | 24341 |
| 1 | MandelPlot.paint | MComponentPeer.paint | 174850 |
| 1 | handleExpose | ?.? | 174896 |
Okay, so what does all of this mean? The following are a couple of observations:
Here are a couple of guidelines for optimizing Java programs. All of these suggestions are equally applicable to Java applets as well as Java applications.
General optimization guidelines:
// Poor: the same value is computed and assigned twice j = 2 + 2; i = j; i = 2 + 2; // Better i = 4;
After studying java.prof, we're ready to hand-tune our Mandelbrot Set. We will not change the basic design. We merely want to target and rewrite those specific parts of the program that seem to be hogs.
Here are specific changes that we made to our final version, Mandel3.java. As you study the program yourself, you will undoubtedly discover other, perhaps better, changes that could be made:
These modifications are shown in Listing 42.20.
Listing 42.20 Mandel3.java-Snippets Showing the Optimizations to our Mandelbrot Program
class MandelPlot extends Canvas
{
private static final int maxcol = 399, maxrow = 399, max_colors = 8,
max_iterations = 512, max_size = 4;
private static Color cmap[];
// Reduce #/setColor and #/drawLine graphics I/O calls
private static Color last_color = Color.black;
private static int last_x = -1, last_y = -1;
private static Graphics graphics_context;
private static final void plot (int x, int y, int color_index)
{
// See if we've moved to a different column
if (x != last_x) {
if (last_x >= 0) {
graphics_context.drawLine (last_x, last_y, last_x, 399);
last_y = y;
}
last_x = x;
last_color = cmap[color_index];
graphics_context.setColor(last_color);
}
else {
if (cmap[color_index] != last_color) {
graphics_context.drawLine (last_x, last_y, last_x, y-1);
last_y = y;
last_color = cmap[color_index];
graphics_context.setColor (last_color);
}
}
}
...
// Avoid the overhead of unnecessary redraw
public final void update (Graphics g)
{
paint (g);
}
...
// Save a few cycles/plot by assigning g here, as a a global
public final void paint (Graphics g)
...
graphics_context = g;
...
As you can see in Figures 42.10 and 42.11, there is a considerable improvement in execution time.
Figure 42.10 : The optimized Java application runs considerably faster (2:28).
Figure 42.11 : Java applets show similar improvement after optimization!
Table 42.8 shows the java.prof execution profile of our optimized
Mandelbrot Set program. Compare this with the unoptimized version
shown earlier in Table 42.7.
| # count | callee | caller | |
| 11604 | pSetForeground | setColor | |
| 11996 | setColor | plot | |
| 11995 | drawLine | plot | |
| 160000 | plot | paint |
Over the course of the past several pages, we've learned the basic mechanics of writing Java applets. Although we've hardly scratched the surface of everything Java is capable of doing for you and your development team, we sincerely hope that you're excited about what you've seen so far.
Unfortunately, nothing is perfect. Not even for a programming language as cool as Java! Let's talk for a few moments about some of the problems a Java programmer might encounter:
With all the hype surrounding Java, there's sometimes a tendency to view Java as a "one-stop solution" to all active content on the Web. This is simply not the case: Java inter-operates well with many other Internet technologies. For example,
Congratulations! You are now a Java applet developer! Well-armed with solid experience, a grasp of the syntactic basics and main programming concepts, and an appreciation for good design habits, you should now be prepared to start developing your own applets and full-blown Web-based applications.
Where do you go from here? Probably the best place to start is
to glance around at some of the exciting things your fellow developers
around the world are doing with Java. Here are a couple of popular
URLs to get you started. Above all, enjoy!
| ON THE WEB |
http://www.javasoft.com Sun's official site for the latest information about the Java language http://www.gamelan.com Arguably the largest and most popular "unofficial" site for the Java programming community. A great site to learn about Java tools, discover other Java-related home pages, and find sample code. news:comp.lang.java Probably the best way to ask and answer Java-related questions from the worldwide Java programming community. |