Check out the new USENIX Web site.
Using JavaUSENIX

 

by Rik Farrow
rik@spirit.com

October, 1996

Often, in this community, suggesting that something should be done is tantamount to volunteering to do it. Well, perhaps this is also true of committees that get things done. In my case, it means that this is the initial article in a series about using Java. In the future, we expect that other writers/programmers will also contribute to this feature.

This article is not a beginning tutorial. It assumes that you know something about Java, while still covering some of the basics for getting started. I suggest Exploring Java (Niemeyer and Peck, O'Reilly and Associates), some of the other books mentioned in ;login: reviews, or the online documentation available from JavaSoft for getting started with Java.

Java has survived well beyond the hype stage. Its initial popularity followed on the coatails of the HotJava World Wide Web browser. Soon, Netscape had decided to support Java applets, and surprisingly shortly after that, even Microsoft licensed Java. HP and IBM are working on Java support (Java will be supported within OS/2 and AIX), and a port has been completed to Linux and many other PC UNIX-clones. In a future feature, I will list pointers to various free versions as they become available. The Usenix Web site would be another good place for pointers.

For now, the place to get a version of Java is ftp.javasoft.com. This server is often busy, and has a limit of 150 simultaneous connections and an annoying short timeout. If you don't get in, a list of mirror sites is listed.

What you want to get is the Java Development Kit (version 1.0.2 at this time). The JDK is free, and versions for Solaris 2.x (both SPARC and x86), Windows 95 or NT, and MacOS 7.5.x are available from Sun. To find versions for other operating systems, try a search engine with the letters jdk.

The Tools

The JDK unpacks into several directories. The previous version, 1.0.1, included HotJava, the source to which may be useful to you if you want to learn how to load Java classes dynamically. I plan to show examples of loading classes over the network in a future article, but mention this now in case you haven't already discarded a 1.0.1 version of the JDK. HotJava is missing from 1.0.2.

You'll want to add the java/bin directory to your PATH. The bin directory contains the executables and the shared objects (which contain references to native libraries). You also want to create a new variable named CLASSPATH, which includes the java/lib directory, any directory you will keep your own classes in, and dot, the current working directory for development (non-developers shouldn't include dot in their CLASSPATHs). For the Windows 95 impaired, such as myself, I'd like to point out that the separator for PATH and CLASSPATH elements is the semicolon, not the colon.

The javac program compiles Java classes. Each class (or perhaps several classes) is contained in a file with the same name as the public class in that file and the .java extension. A successful compilation results in a .class file for each class defined in the .java source file.

When you compile using javac, you must include the .java extension in the filename. Common mistakes include not using the same filename as the public class defined in the .java file, and not having CLASSPATH correctly defined.

In the JDK, the Java compiler is actually a Java application. This makes it easier to port Java to other environments, but at the cost of some performance. I have been told that Microsoft's J++ compiles Java source much faster, and I suspect that Microsoft, unconcerned with portability issues, has written their compiler in native code. Other development toolkit vendors will likely follow suit. Not that compilation takes terribly long anyway on fast systems.

The java program implements the Java Virtual Machine. The Java Virtual Machine turns Java bytecodes into native machine code and interfaces with the native operating system. In some implementations of the JVM, you can optionally produce "just-in-time" compiled code, which means that the first pass through the interpreter results in machine code which speeds up subsequent execution. For a lot of what is done with Java, such as user interfaces, a "just-in-time" compiler is overkill. Remember that this compiler is part of the runtime environment, not the compiler which produces the classes.

The JDK also includes an appletviewer, which can view remote or local files (using either the file or http style URL's) and interpret <applet> tags. Of course, both Netscape Navigator and Microsoft Explorer also support the <applet> tag, but the appletviewer is still nice for testing purposes.

Applications vs Applets

The end product of compiling Java source code is always one or more class files. If the class is intended for standalone use (an application), it must have a class method named main(). In Java, class methods (those associated with the class rather than instances of a class) are labeled with the keyword static. The JVM, started via the java command, creates an instance of the class provided as its argument, starts a thread for this class instance, then invokes the main() method for this class with an array of strings as an argument.

Applets work a little differently. The browser or appletviewer loads the class file when it interprets the <applet> tag with a special class loader (which treats classes differently than locally loaded classes). After loading the class file, browsers go through another step, byte code verification, under the assumption that remotely created code may be malicious or not compiled with standard tools, leading to overwriting the stack, crashing the browser, and other mischief. If the class passes, an instance of the class is created, and a thread started for this class.

Unlike standalone applications, a browser does not call main(), but instead makes a series of calls to the applet's methods. The init() method is called once, after the thread has been created, followed by start(). The start() method is recalled anytime the Web document containing the <applet> tag is revisited, and the stop() method is called when another Web document is loaded. The paint() method gets called when the applet's Panel gets exposed, for example by scrolling up or down through the Web document. The paint() method can also be called directly, or via repaint() or update() calls within the applet.

Going Both Ways

Examples often make things clearer, but I take a risky approach in the following example. Clock.java contains a class which can be used as a standalone application or as an applet, and works similarly either way. Through using this example I plan to compare and contrast the way applications and applets work, and how they are written. (Click here for a complete copy of Clock.java)

The source file begins with an <applet> tag wrapped in a C-style comment. This is a trick I learned from the Patrick Naughton book (The Java Handbook, Osbourne McGraw-Hill, 1996). If you include an &l;applet> tag in a comment, you can then test the applet within the appletviewer without writing a separate Web document (HTML file). For example,

    appletviewer file://home/rik/Clock.java

will start an appletviewer, which will in turn load the Clock.class file if it is found in the same directory (or you can specify a different directory in the <applet> tag).

       
/* <applet code=Clock.class
              height=110 width=200>
   </applet>
 */

import java.awt.*;
import java.util.Date;
    
The import keywords tell the compiler where to look for non-locally defined methods or classes. Unlike C or C++, there are no #include statements, and importing a Class doesn't include its bytecode, but only makes the compiler aware of it. Using an asterisk means that all classes in that package (or directory) will be included, which can slow down compilation.

The class keyword marks the beginning of the class definition. There can be at most one public class for source file. This example includes a second, non-public class later on. The instance variables will be available to each instance (created using the new keyword and the Class name). Static, or class variables, exist as part of the class, and are available before any instances have been created.

    
public class Clock extends java.applet.Applet
  implements Runnable {

// Instance variables
Thread aThread; // Handle for Thread object
Date date;      // Handle for Date object
Frame frame;    // Handle for window (Frame object)
Graphics graphics;  // Handle for drawing graphics

// A class variable to distinguish between Applet
// and the standalone version
private static boolean isApplet = true;

A public method with the same name as the class being defined is called a constructor. The constructor is always called when a new instance is created. The first statement within a constructor, which may be implicit (as in this example), is a call to super(), the constructor for the superclass. The superclass, in turn, also calls its super(), and so on, up to the root class, Object.

In a constructor, you usually include initialization for the instance just created. Here, I must distinguish between when the constructor is called from main(), the standalone version, or from within an applet, using the class variable isApplet. Equivalent initialization for the standalone version is performed in the constructor for MakeWindow.

The Panel instance is created to hold the two Buttons, and then the Panel p is added to Panel which the browser has created for the applet. Applets are subclasses of Panel, so the method invocation add("North", p) applies to the new instance of Clock, a subclass or Applet, which is a subclass of Panel. The Panel's add method handles adding the Panel as another Component.

    
public Clock() {
  // Standalone version uses MakeWindow()
  if (isApplet) {
    Panel p = new Panel();
    p.add(new Button("Start"));
    p.add(new Button("Stop"));
    add("North", p);
   }
}
    

The next three methods will get called by the browser automatically after loading the applet. The start() method is also called directly from main() in the standalone version, and by the event handling routines to start the thread. The thread is created in start() with this, a reference to the instance through which the method was invoked, making the instance of the Applet the target of the thread.


public void init() {
  graphics = getGraphics();
  date = new Date();
}

// start() is called when applet's
// panel becomes visible

public void start() {
  if (aThread == null) {
    aThread = new Thread(this);
    aThread.start();
  }
}

// stop() is called when the
// appletviewer changes pages

public void stop() {
  if (aThread != null) {
    aThread.stop();
    aThread = null;
  }
}
    

Threads get started or stopped via calls to their start() and stop() methods (they can also be suspended or resumed). Starting a thread causes its run() method to be invoked. When the run() method exits, the thread dies. Here, the applet's start() and stop() methods (above) call the thread's start() and stop() methods to start the thread, or destroy it.


public void run() {
  while (true) {
    date = new Date();
    paint(graphics);
    try {Thread.sleep(1000); }
    catch (InterruptedException e) {};
    }
}
   

The paint() method gets called when the applet needs to be displayed, or when called explicitly from the run() method. The Graphics object provides an interface to the underlying toolkit used to display graphics in the local windowing environment (another topic in itself). Here, I use clearRect() to erase the region where the date will be displayed, and drawString() to display the date. Date objects contain the date as a long (64 bit value representing the number of milleseconds since the Epoch), and the toString() method converts this to a more readable String.

   
public void paint(Graphics g) {
  g.clearRect(25,25,150,50);
  g.drawString(date.toString(), 25, 60);
}
   

Because I have chosen to make this both an application and an applet, I needed two ways of handling user events. The action() method gets called by the handleEvent() method of the Panel with two arguments anytime either of the two buttons, Start and Stop, get selected. The applet's start() or stop() methods get called, starting or stopping the thread.

  
public boolean action(Event e, Object arg) {
  if (e.target instanceof Button) {
    if (((String)arg).equals("Stop")) stop();
    else start();
    return true;
  }
  return false;
}
  

For the standalone version, we need a couple of things. First, we must have a main() method, defined as static (a class method). I set the class variable isApplet to false before creating the Clock object so the constructor will not create the Panel and two buttons. Then a MakeWindow object is created, which creates a standalone window (Frame object), and adds the Panel with the Start and Stop buttons.

Notice that most of the methods and variable references in main() are preceded with an object handle. In Java, all references to non-static methods and variables are associated with an object--when no object is specified, this, the instance the method was called through, is implied.

   
public static void main(String args[]) {
  isApplet = false;
  Clock clock = new Clock();
  clock.frame = new MakeWindow("Clock Window", clock);
    clock.graphics = clock.frame.getGraphics();
    clock.start();
  }
}

// A second helper class; could be in a separate file
class MakeWindow extends Frame {

// Instance variable
Clock clock;

// Constructor for new MakeWindow object
public MakeWindow(String name, Clock applet) {
  super(name);
  clock = applet;
  Panel p = new Panel();
  p.add(new Button("Start"));
  p.add(new Button("Stop"));
  add("North", p);
  resize(200, 110);
  show();
}

The MakeWindow instance has its own Frame, and must also have its own event handler. Instead of using action(), the handleEvent() method for the Frame class is overridden. The action() method would handle the Button events, but there is no "shortcut" method for window destroy events (choosing Close or Quit from the Window's menu). I was a bit surprised to find that the toolkit handles events such as resizing or minimizing Java created Frames, but not quitting them. You must capture WINDOW_DESTROY events, and do the work yourself.


public boolean handleEvent(Event e) {
  if (e.id == Event.ACTION_EVENT) {
    if (e.target instanceof Button) {
      String s = ((Button)e.target).getLabel();
      if (s.equals("Stop")) clock.stop();
      else clock.start();
    }
  }
  else
  if (e.id == Event.WINDOW_DESTROY) {
      this.hide();
      System.exit(0);
  }
  else
      super.handleEvent(e);
  return true;
  }
}

Future Events

There is much to learn about in Java. Although this example is a bit long, I hope to provide some shorter and yet compelling examples in future features, along with contributions from other Java programmers. If you have never looked closely at Java, this example may do more harm than good. It only hints at the power you get from using some of the predefined classes which come as part of the core Java API. The ability to use ready-made classes is where the real future of Java is, and the real future for the programmers and companies who will be designing and writing those classes and packages.

First published in ;login:, Volume 21, No. 5, October 1996.

 

?Need help? Use our Contacts page.
Last changed: May 16, 1997 pc
Java index
Publications index
USENIX home