|
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 CLASSPATH s).
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.
| |