Adventures of a Java Beginner David L. Zellmer, Ph.D. ABSTRACT The presenter is an experienced user of the Web for providing student content, and has used conventional programming languages in the past. The task, using Java, was to turn an existing animated Web figure into an student-interactive one by writing a Java Applet. Books promising to teach Java in "21 days" were used in an attempt to learn this object-oriented, multithreaded language. The presentation will cover what it takes to get started in Java, what kinds of software and hardware will be needed, and what the unforeseen pitfalls are for the beginning Java programmer. For an annotated listing of the complete source code for the working version of the BohrAnim applet, click here.THE PROBLEM An existing animated gif had been produced using GifBuilder showing the relationship between the Bohr energy levels and the light that was given off. This animation just repeated in an infinite loop, not allowing me to pause and comment on each stage in the process. I used GifBuilder to break up this animated gif back into its component gif image files. I now needed a programming language to create an array of these images, then manipulate them to produce the effects I wanted. I had previously done some simple animations on web sites using JavaScript, but this simple scriptable addition to HTML, which is not related to Java the language, by the way, did not have the data structures or control features I needed. WHAT IS JAVA? Java is a complete object-oriented language with many features in common with C++. It contains a host of built-in application programming interfaces (API's) designed for Web-based use. The programs written in Java are of two main types: Stand-alone Java programs that will do anything a standard language can do, and Java Applets which are designed for use on the Web, and which have security features which prevent these applets from accessing or modifying the contents of a web client's hard drive. Java is designed to be platform-independent; i.e., it will execute on nearly any computer type that can access the Web. Here is what the files look like that reside on the server hosting my chemistry web site. The applet called "BohrAnim.class" can read the image files in the folder "pix" and generate the graphics I want. The HTML file BohrApplet.html calls the applet and puts it where I want it on the web page. Bohr.html and Bohr.gif are the old files that just display the old animated gif. Here is what BohrApplet.html looks like. The code that calls the applet is in boldface.
The java applet is called from HTML in much the same way that you would insert an image on a web page. The height and width are specified, and the image generated by the applet goes wherever the surrounding HTML would normally place it. The only visible difference between this applet and the earlier animated gif is the row of buttons along the bottom. The beauty of Java is that the file BohrAnim.class is in something called "bytecode" which can be placed on almost any computer platform. In this example I had created the bytecode file on a Macintosh, checked its operation as a local file with Netscape Navigator, then uploaded it to the Unix server that hosts my web site. I tested the applet on my web site using a colleague's PC running Windows and Internet Explorer. Another colleague checked the applet on her Unix workstation. It ran on all three platforms. Each computer has an application called the Java Virtual Machine (JVM) either in its operating system or as part of the web browser used. If you have been using the web, the JVM already exists on your machine. The JVM translates the bytecode into the commands needed by the particular computer. Because of this translation, Java won't execute as rapidly as C++ code compiled for a particular platform, but makes up for this by the "write once, run anywhere" nature of its code. Be aware, however, that this does not always work perfectly. Some Windows-specific API's have sneaked into the class libraries, for example, sometimes forcing us to quit out of Netscape Navigator and call the page up again in Internet Explorer. WHAT DO YOU NEED TO WRITE A JAVA APPLET? I used CodeWarrior, an Integrated Development Environment (IDE) from Metrowerks (www.metrowerks.com) that makes writing and debugging the code very convenient. The CD-ROMs that come with CodeWarrior include tutorials, extensive documentation, many examples, the java class libraries, the Java bytecode compiler and several applications that help you write and run the code. A full installation of CodeWarrior, which included C++ as well as Java, took 450 MB on my Macintosh G3. There is a PC version available too, of course. Metrowerks also sells an "Explore Java" version to the general public for well under $100, but there are restrictions on the use of what you develop. Academic pricing for the full unrestricted version was under $120, which is what we bought. See their web site for details. For those who just want to try Java before investing in a commercial IDE, go to the Sun Microsystems Java website at http://java.sun.com where you can download the Macintosh Runtime for Java Software Development Kit (MRJ SDK). A PC version is also available. The Sun site also includes an extensive tutorial on writing code in Java, including a "getting started" tutorial for beginners that even takes you through the download. Other resources include documentation for the Java class libraries, as well as numerous examples of Java applications. There are no charges for the download, and the sizes of the files are quite modest. The decompressed MRJ SDK 2.2 on my Macintosh takes up 30 MB of disk space and was a 7.8 MB compressed download in February of 2000. DO YOU HAVE ANY BOOK RECOMMENDATIONS? I used two books to help me get started. Teach Yourself Java for Macintosh in 21 Days, by Laura Lemay, Charles L. Perkins, and Timothy Webster, Hayden Books, 1996, and Java Examples in a Nutshell by David Flanagan, O'Reilly & Associates, Inc., 1997. These books have many useful examples. Once I wrote, compiled and ran some beginning applets and had the basics down, the examples helped more than the explanatory text. The Java specification continues to grow, adding new features and deprecating old ones. I tried to stay with the older Java 1.1 specification since it seemed to work OK. Examples from the 1996 Lemay book still mostly worked, although there were many errors in the very beginning examples, which was unfortunate. BohrAnim is based on the "Neko" animation example and on the descriptions of button panels done later in the book. Customer reviews of the Lemay book on amazon.com trashed it badly, mostly because of the nearly useless software that came on the CD-ROM. This book is still available today (January 2001). You might want to try something else to get started, but the examples of graphics, animation, and button controls are good. I'M READY TO WRITE MY FIRST APPLET. WHAT DO I DO? Both CodeWarrior and the Sun Software Development Kit come with excellent "getting started" tutorials. I got a bit lost the first time, but figured things out. You can too. I do have a programming background in FORTRAN, BASIC, and Pascal, but had not written any serious code for years. Even so, if you read a book that says something like "Java is harder than HTML but easier than doing your own income tax," don't believe it. Writing a simple "Hello, world!" applet is easy enough, but doing an entire project is far from trivial. Since examples are the best way to learn something new, here is where I started on BohrAnim. The code below just reads one image and displays it. There are no animation threads or control buttons. I copied the code from an example and modified it to fit my filenames. I didn't know what half the commands meant yet. It worked.
This is just a text file stored as BohrAnim.java. You could write it using Microsoft Word, and store it as a text file. You could download a free copy of BBEdit Lite from www.macworld.com and use that to generate the text files. The one above was written using the custom text editor that came with CodeWarrior. The convenience of the CodeWarrior editor is that it color-codes Java keywords and maintains the indented formatting that makes the code easier for humans to read. BBEdit Lite also has some formatting features that make writing Java text files or HTML text files a bit more convenient than using a regular word processor. To run this applet, you first have to tell CodeWarrior to run it through the Java bytecode compiler, producing a file called BohrAnim.class, then use the included applet runner to display the results. There are several steps to setting up the first compiler pass, but the tutorials will walk you through it. Once set up, it takes only one mouse click to compile and run your modified code. Alternatively, you could drag this text file onto the "javac" compiler in the Sun SDK to produce the BohrAnim.class bytecode. If you don't have an Applet Runner, just call this bytecode file from your web browser using the embedded <applet></applet> tags shown at the start of this article. WHAT AM I SEEING HERE? WHAT MAKES THIS AN OBJECT-ORIENTED PROGRAM? You will need to read the books to get a full description of what it means to program in an object-oriented language like Java. Back in the old days I walked two miles through the snow to turn in my punched cards at the computer center, then came back three days later to pick up the paper printout. We only had to know in detail the structure of our data files, then write a set of procedures to process these data files by reading them, transforming the data, then sending the output to the printer. The languages came with a set of built-in procedures for doing commonly used tasks. As a programmer, you had to know what went into a procedure and what came out. Things happened in an orderly fashion, one after the other. Today's computer has a Graphical User Interface (GUI) with which we interact using keyboard and mouse. The screen is in a constant state of refresh as we mouse click, type, bring up new windows from our hard drives or from the Internet, and flit from one window to the next to find things. We scroll and push buttons. We scan things in, and send images out via e-mail. In short, we interact with a screen full of objects, each busily doing its own task, seemingly at the same time that other objects are doing theirs. Languages such as Java or C++ are object-oriented and multi-threaded to deal with this environment. In my complete BohrAnim applet, the objects include a graphics window to display my nine pictures, and some buttons to start, pause, and step the display of these images. The threads come into play when we consider that (at least) two things have to happen at the same time: (1) paint the images onto a graphics window at the rate I had specified, and (2) keep track of which buttons are being pushed to change the conditions. The biggest hurdle I faced in producing the final version of BohrAnim was getting these two threads to work properly. I had to handle the (1) graphics animation thread myself, while the Java system event handler took care of the (2) button/scrollbar control thread and anything else that was going on. A class is a collection of methods (what I used to call procedures) and data that lets us create and work on the objects in our computing environment. When we write a Java applet, we "extend" the set of classes to include what we want done. At the start of the simple applet above, we first import those parts of the Java class library that we need to accomplish our task. "Image" and "Graphics" are part of the "awt" or Abstract Window Toolkit that contains all the objects we see on our GUI. After declaring some variables and specifying that we are going to create a new class which inherits the capabilities of the superclass "Applet" we then invoke two methods to (1) read our image file from disk, then (2) paint it onto a graphics output window. The syntax of the commands borrows a lot from the language "C" and should be familiar to anyone with some programming experience. The "dot" notation as in "iheight = bohr1.getHeight(this)" has to do with how attributes of an object can be extracted from the object itself. In this case we extract the height of the current (this) graphical object in pixels and assign it to the variable "iheight." THIS LOOKS MUCH TOO COMPLICATED. CAN I GO NOW? When I first tried Java from that 1996 book, Teach Yourself Java for Macintosh in 21 Days, by Lemay, Perkins, and Webster, I was frustrated by buggy and crippled Java production software that came on the included CD-ROM. The errors in the beginning examples didn't help either. I could get a few simple Java programs to run, but was prevented from producing anything I could actually put on the Web, unless I paid more for a commercial version of something called "Roaster." I tried downloading the SDK from Sun, but couldn't get it to work either. I put any further Java projects off for at least two years. This old Lemay book is still available (January 2001) according to amazon.com. Customer reviews were very negative, however. I should have started with something else. On the other hand, BohrAnim is based mostly on animation threads and button handling examples from this book. Today, CodeWarrior worked right out of the box. A new download of the Sun SDK worked also. Armed with some good animation examples from later chapters in the Lemay book, I figured I could easily modify them to do what I wanted. With the development software finally working, production of my first real Applet turned into a puzzle that I was motivated to solve. In the sections that follow, I'll describe those things that gave me the most problems -- graphics and threads. If you want to do similar things, my descriptions may save you some time. WHY ARE GRAPHICS A PROBLEM? DON'T YOU JUST SEND COMMANDS TO A SCREEN? Back in the Olde Days I wrote graphics animation programs for the Apple II. Its screen was split into two areas: text and graphics. The graphics area was simply a reflection of an area of memory where I could turn pixels on or off to create the images I wanted. Anything typed to the keyboard showed up in the text area. I could trap keystrokes there to allow me to control what was painted to the graphics area. I once wrote a chromatography simulation for the Apple II in about a day. I'm still working on the Java equivalent of that animation. The new complexity comes from the Graphical User Interface (GUI). Whatever you write to the screen must coexist with everything else that is going on. Look at the screen on your computer. All those windows and icons are separate objects that are managed by handlers that are part of your operating system. The operating system on my old Apple II took about 16K of RAM. The Macintosh OS 8.6 (already old) is currently using 23 MB as I type this. Simple static graphics in Java are not that hard to do. What took me time to figure out was the set of interlocking methods known as repaint(), update(), and paint(). Updating graphics happens constantly in a GUI. If you move the window for your applet, the background beneath it must be restored. If you open a window on top of your applet, the applet window must be restored when you bring it back to the front again. Your OS, your browser, and the Java system manage all this for you. You just have to work with it a little. I confess that this is not all clear to me. I used examples of animation that worked, then changed them to do what I want. A real Java programmer would probably find my code filled with very inelegant structures. If you look at the BohrAnim source code, you find that paint() is not called directly. Instead you will find that we load an Image variable called "currentimage" with the next frame of our animation, then call repaint(). Repaint() calls update() which erases the applet drawing area; update() then calls paint() which draws in the new image. To reduce screen flicker, we rewrite update() so it calls paint() without first erasing the screen. These little details can drive you nuts. WHAT PROBLEMS DID YOU HAVE WITH THE THREADS? Problem 1: I didn't think I needed to deal with the threads at first. In previous programs all I had to do was keep a loop going that showed the animation frames one after the other. That would be "run" mode. To change the rate of the animation I just put a delay loop in the animation loop to slow it down, then changed the variable that set the delay time. To "pause" an animation I just kept testing a Boolean variable that broke the loop when it changed from "keep going" to "stop now." Once stopped I could "step" an animation by calling a procedure that advanced the graphics by one frame, but did not loop. None of these worked in Java, because a Thread Manager that seemed dedicated to ignoring all my attempts to change things during program execution controls the order of things happening in Java. Some of my more drastic attempts to force a break in the animation loop led to something called "thread death" that killed Java itself and took my computer with it in a system crash. "Open the pod bay doors, Hal." "I'm sorry, Dave, but I can't do that." A re-boot brought my computer back for another try. Problem 2: So where is the thread that I can control? There are entire books dedicated to thread handling in Java. The animation examples I found ranged from seeming to have no threads at all, to spawning multiple threads that generated little "sprites" that floated around the screen, each doing its own thing. I was baffled. A limited solution to my problem came with the description of the "Runnable" interface, which seemed designed for simple-minded beginners like myself. The first lines of my working version of BohrAnim are: import java.awt.*; //just load the whole Abstract Window Toolkit (awt) class public class BohrAnim extends java.applet.Applet implements Runnable { With "Runnable" implemented, I can now put a method in my applet called "run()." Anything I put into "run()" becomes part of this one thread that I can define myself in this particular class. If you check the BohrAnim source code you find that I called my thread "runner." I got "runner" going in a start() method, and killed it off permanently in a stop() method. In the run() method I placed my animation loop, complete with a delay time I could control from outside with a global delay time variable. This thread was MINE! All the other threads, such as those tracking where my mouse was, or if I had clicked a button or dragged a scrollbar, were beyond my control. I could only hope that the Thread Manager would "see" what I was dragging or clicking and get word to my thread to do something I wanted. Problem 3: How can I be sure my thread will do what I want, and how do I send it messages? In my previous programming life, I made sure I completely understood what each procedure did and completely debugged it before adding it to the overall program. That way there would be no nasty surprises, otherwise known as "bugs." The data sent into each procedure and the results that were returned were carefully defined and controlled. In Java I put a message in a bottle, tossed it into a frothy sea, and hoped it would find its way to my thread. Then I hoped my thread might do something. This is NOT the way I was taught programming! If you check the BohrAnim source code, you will find a number of Global variables that will be visible to all the methods in the BohrAnim class. So much for controlling what goes into and out of my methods, violating Rule 1 of good structured programming practice. Next we have a bunch of methods listed in random order that activate depending on what is happening in the outside world. So much for controlling the order of execution. I had three messages I could put in my bottle. (1) Change the value of "delay" anywhere at all. (2) Execute the method "runner.suspend()" anywhere at all. (3) Execute the method "runner.resume()" anywhere at all. At least one Java source warned me that use of ".resume()" and ".suspend()" is now considered bad programming practice and can lead to unpredictable results. "Threads should start and stop 'naturally'," said the source. Stealing code from other Java examples, I wrote handlers for the scrollbar and buttons that were to control my animation. I made the value of "delay" larger using scrollbar handlers. The animation slowed down. I pressed a button that activated a button handler that called a method that executed runner.suspend(). The animation loop stopped! I clicked another button that led to runner.resume(). The animation started up again. Success! (sort of). See the BohrAnim source code for details. SO, DO YOU NOW KNOW HOW JAVA ACTS AND HOW TO WRITE GOOD PROGRAMS IN IT? No. But I can fake it. |