Java


Java Language


CLASSPATH and Packages

In a language like C or C++, the compilation model is fairly simple. You compile a source file to an object file, and then later invoke a linker to combine the object files with libraries into an executable program. More recently, shared libraries and so forth have been used in the compilation model.

Java is a bit different in its approach. We will be describing how it handles program packaging and compilation in this and subsequent issues.

The first thing to mention is that the Java compiler (javac) will compile more than just the one source program given to it on the command line. It will go off and do other compilation as necessary. The result of compilation is an xxx.class file, containing the bytecodes and a description of the interface (set of methods and so on) offered by the class contained within.

A public class should be defined within a file of the same name as the class, with lower/upper case significant. A public class uses the keyword "public". An example of a public class is given in the above example on applets. "ga" is a public class, "AppFrame" is not. We will mention more about public classes in a moment. A Java source file may contain only one public class definition.

The next item of importance is the CLASSPATH environment variable. This is a set of directories that is used by the compiler and interpreter to find classes. For example, in a Windows NT installation, the setting might be:
        CLASSPATH=".;d:/java/lib"

meaning that the current directory and then the directory d:/java/lib are searched. In UNIX the separator is ":" instead of ";".

Searched for what? Source files, class files, and packages. What are packages? Packages are groupings of related classes. If I say:
        // file A.java

        package X;

        public class A {}

        // file B.java

        package X;

        public class B {}

then A and B are grouped in the package X. A somewhat similar feature in C++ is namespaces.

Packages are tied in with the CLASSPATH variable and the file system. Declaring a package X as in the example means that somewhere along the CLASSPATH directories there will be a directory X, with files A.java, B.java, A.class, and B.class in it. If the current directory is first in the CLASSPATH list, then this means creating subdirectories of the current directory, each subdirectory as the name of a package.

This works for system directories as well. For example, looking under d:/java/lib, there are directories java/applet, java/awt, java/io, java/lang, and so on. These tie in directly with import directives of the form:
        import java.io.*;
        import java.awt.*;

and so on. Note that:
        import java.lang.*;

is supplied implicitly and so does not have to be specified by the programmer. "*" means to import all classes in the package.

Package names can be used to qualify program entities. For example, I can say:
        java.lang.System.out.println("Howdy!");

In fact, there is some discussion about making the highest levels of the package hierarchy correspond to Internet domain names, as in:
        COM.glenmccl.java.lang.System.out.println("Howdy!");

If this is done, then you won't have to worry about creating packages and classes that interfere with those produced by others!

We mentioned above the distinction between public and non-public classes. Non-public classes in packages cannot be imported into a program. For example, this sequence is illegal:
        // file y1.java in Z/y1.java relative to current directory

        package Z;

        /*public*/
        class y1 {}

        // file y2.java in current directory

        import Z.*;

        public class y2 {
                public static void main(String args[])
                {
                        y1 y = new y1();
                }
        }

To wrap up this discussion for now, here is a longer example. There are two classes that do statistical analysis, one for descriptive statistics like mean and standard deviation and the other for doing correlation. Don't worry too much if you don't know statistics; this example is really about packaging.

We use a package called Stat for grouping these classes.
        // file Stat/descr.java in subdirectory

        package Stat;

        public class descr {
                private long n;         // count of numbers seen
                private double x;       // sum of X
                private double x2;      // sum of X^2

                // constructor
                public descr()
                {
                        n = 0;
                        x = x2 = 0.0;
                }

                // add a number to the pool
                public void add(double d)
                {
                        n++;
                        x += d;
                        x2 += d * d;
                }

                // retrieve the count of numbers seen
                public long cnt()
                {
                        return n;
                }

                // return mean (average)
                public double mean()
                {
                        if (n < 1)
                                throw new ArithmeticException();
                        return x / (double)n;
                }

                // return standard deviation
                public double stdev()
                {
                        if (n < 2)
                                throw new ArithmeticException();
                        double d1 = (double)n * x2 - x * x;
                        double d2 = (double)n * (double)(n - 1);
                        return Math.sqrt(d1 / d2);
                }
        }

        // file Stat/corr.java in subdirectory

        package Stat;

        public class corr {
                private long n;         // number of values seen
                private double x;       // sum of X
                private double y;       // sum of Y
                private double x2;      // sum of X^2
                private double y2;      // sum of Y^2
                private double xy;      // sum of X*Y

                // constructor
                public corr()
                {
                        n = 0;
                        x = y = x2 = y2 = xy = 0.0;
                }

                // add in a pair of numbers
                public void add(double a, double b)
                {
                        n++;
                        x += a;
                        y += b;
                        x2 += a * a;
                        y2 += b * b;
                        xy += a * b;
                }

                // return count
                public long cnt()
                {
                        return n;
                }

                // get correlation
                public double getcorr()
                {
                        if (n < 2)
                                throw new ArithmeticException();
                        double d0 = (double)n * xy - x * y;
                        double d1 = Math.sqrt((double)n * x2 - x * x);
                        double d2 = Math.sqrt((double)n * y2 - y * y);
                        return d0 / (d1 * d2);
                }

                // get a for y = ax + b
                public double geta()
                {
                        if (n < 2)
                                throw new ArithmeticException();
                        return ((double)n * xy - x * y) /
                            ((double)n * x2 - x * x);
                }

                // get b for y = ax + b
                public double getb()
                {
                        if (n < 2)
                                throw new ArithmeticException();
                        return y / (double)n - x / (double)n * geta();
                }
        }

        // file stest.java in current directory

        import Stat.*;

        public class stest {
                public static void main(String args[])
                {

                        // test descriptive statistics

                        descr sd = new descr();
                        for (int i = 1; i <= 10; i++)
                                sd.add((double)i);
                        System.out.println("count = " + sd.cnt());
                        System.out.println("mean = " + sd.mean());
                        System.out.println("std dev = " + sd.stdev());

                        // test correlation

                        corr co = new corr();
                        for (int j = 1; j <= 10; j++)
                                co.add((double)j, (double)j * 2.5 + 9.0);
                        System.out.println("count = " + co.cnt());
                        System.out.println("correlation = " + co.getcorr());
                        System.out.print("y = " + co.geta());
                        System.out.println("x + " + co.getb());
                }
        }


Anonymous Classes

Java 1.1 also introduces inner classes, which are classes defined within another class. This is a complicated topic that we will spend several issues covering in full. One type of inner class is an anonymous class, which we can illustrate with an example:
        import java.awt.*;
        import java.awt.event.*;

        public class button {
                public static void main(String args[])
                {
                        Frame  f = new Frame("testing");
                        Panel  p = new Panel();
                        Button b = new Button("OK");

                        b.addActionListener(
						        new ActionListener()
								{
                                        public void actionPerformed(ActionEvent e)
                                        {
                                               System.err.println("got here");
                                               System.exit(0);
                                        }
                                }
						);

                        p.add(b);
                        f.add(p);

                        f.pack();
                        f.setVisible(true);
                }
        }

This example sets up an AWT frame with a panel, and adds a button containing "OK" to the panel. When the button is selected, the program terminates. With the 1.1 event delegation model, we want to bind an action listener to the button, such that a method actionPerformed() will be called when the button is selected. That is, we want to implement the interface ActionListener.

This could be done via a separate class, or by having button implement ActionListener. We've seen examples of such usage before. But a more contained approach is to use an anonymous class. When I said:
        new ActionListener() { ... }

I in fact declared a new class, with no name, that implements the interface ActionListener. To actually implement this interface, actionPerformed() must of course be defined.

If ActionListener was a class, rather than an interface, saying something like:
        new ActionListener(arg1, arg2, ...) { ... }

would declare a subclass of ActionListener, and the arguments would be passed to the superclass constructor.

An anonymous class has no name, and therefore has no constructor. An anonymous class may refer to fields and methods in its containing class.

Whether anonymous classes are a "good" feature is a hard one to call. They are very convenient, as this example illustrates. But they make code harder to read, cause problems for debuggers and documentation tools, and so on.


Nested Classes

Another type of class is what is called a nested class, where a class is declared within another class as a sort of helper. Nested classes are declared using the "static" modifier. To see what this looks like, consider an example of managing school records consisting of name/grade pairs:
        import java.util.Vector;

        public class RecordList {

                private Vector recs = null;

                public RecordList()
                {
                        recs = new Vector();
                }

                public void addRecord(String name, int grade)
                {
                        recs.addElement(new rec(name, grade));
                }

                public void dumpAll()
                {
                        int sz = recs.size();
                        for (int i = 0; i < sz; i++) {
                                rec r = (rec)recs.elementAt(i);
                                System.out.println(r.name + " " + r.grade);
                        }
                }

                public static void main(String args[])
                {
                        RecordList rl = new RecordList();

                        rl.addRecord("Jane Smith", 57);
                        rl.addRecord("John Jones", 43);
                        rl.addRecord("Nancy Williams", 51);

                        rl.dumpAll();
                }

                private static class rec {

                        private String name = null;
                        private int grade = 0;

                        private rec(String n, int g)
                        {
                                name = n;
                                grade = g;
                        }
                }
        }

There is a public class RecordList that we create an instance of and then feed name/grade pairs to. It in turn uses a helper class "rec" to actually record the pairs. The individual records are stored in a Vector object for later retrieval.

rec is a separate class, nested within RecordList. It has access to the static members, if any, of its containing class. But it does not have access to the non-static members in RecordList, because this would have no meaning -- object instances of rec are created independently of those of RecordList (another variation, the member class, does allow such access).

There are various other ways in which this application could be implemented. For example, rec could be broken out as a separate top-level public class. But sometimes it's better to hide a helper class, if its use is limited to a narrow application.

The 1.1 edition of David Flanagan's "Java in a Nutshell" has a good discussion of nested/member/local/anonymous classes.


Local Classes

In the previous two issues we discussed the use of anonymous and nested classes. These provide for the nesting of one class inside of another. Another variation on this feature is local classes, where a class is defined within a method.

As a simple example of how local classes work, consider an application that needs to create a listener for an AWT object:
        import java.awt.*;
        import java.awt.event.*;

        public class local1 {

                public static void main(String args[])
                {
                        Frame f = new Frame("testing");
                        Panel p = new Panel();

                        class listen implements ActionListener {
                                public void actionPerformed(ActionEvent e)
                                {
                                        System.exit(0);
                                }
                        }

                        Button b = new Button("Exit");
                        b.addActionListener( new listen() );

                        p.add(b);
                        f.add(p);

                        f.pack();
                        f.setSize(100, 100);
                        f.setVisible(true);
                }
        }

For our example, the listener must implement the ActionListener interface, and define actionPerformed(). But realize that the listener class could be local1 itself, a top-level non-public class in the same compilation unit, an anonymous class, or a local class. We have chosen the last of these.

A local class is visible only within the method where it's defined, and cannot have public/protected/private modifiers on the class definition (they wouldn't mean anything). Such a class can use fields from its enclosing class, and final (unchangeable) local variables from its enclosing method. For example:
        public class local2 {

                private static int a = 37;
                private int        b = 47;

                public void f()
                {
                        int       c = 57;
                        final int d = 67;

                        class Z {
                                Z()
                                {
                                        System.out.println(a);
                                        System.out.println(b);
                                        //System.out.println(c); // error
                                        System.out.println(d);
                                }
                        }

                        Z z = new Z();
                }

                public static void main(String args[])
                {
                        local2 x = new local2();
                        x.f();
                }
        }

The reason that non-final local variables are not allowed is because the implementation of local classes makes a hidden copy of local variables for use by the class. So the local class is accessing a copy of the local variables rather than the variables themselves, and thus only access to unchanging variables makes sense.

Anonymous and local classes have some overlap, and both can be used to solve similar problems. An anonymous class is somewhat cryptic in nature, while a local class is more verbose and easier to understand.


Final Classes

In Java, a class organization such as:
        class A {}

        class B extends A {}

results in a superclass (A) and a subclass (B). References to B objects may be assigned to A references, and if an A reference "really" refers to a B, then B's methods will be called in preference to A's. All of this is a standard part of the object-oriented programming paradigm offered by Java.

But there is a way to modify this type of organization, by declaring a class to be final. If I say:
        final class A {}

then that means that A cannot be further extended or subclassed.

This feature has a couple of big implications. One is that it allows control over a class, so that no one can subclass the class and possibly introduce anomalous behavior. For example, java.lang.String is a final class. This means, for example, that I can't subclass String and provide my own length() method that does something very different from returning the string length.

There is also a big performance issue with final classes. If a class is final, then all of its methods are implicitly final as well, that is, the method is guaranteed not be overridden in any subclass. A Java compiler may be able to inline a final method. For example, this program:
        final class A {
                private int type;
                public int getType() {return type;}
        }

        public class test {
                public static void main(String args[])
                {
                        int N = 5000000;
                        int i = N;
                        int t = 0;
                        A aref = new A();
                        while (i-- > 0)
                                t = aref.getType();
                }
        }

runs about twice as fast when the class is declared final.

Of course, much of the time it's desirable to use the superclass / subclass paradigm to the full, and not worry about wringing out the last bit of speed. But sometimes you have heavily used methods that you'd like to have expanded inline, and a final class is one way of achieving that.


Member Classes

In previous issues we've discussed nested, local, and anonymous classes, all part of the new inner class feature of Java 1.1. There is one more type of inner class to consider, the member class.

A member class is a class defined within another class. Each instance of the member class is associated with a corresponding instance of the defining class. Unlike a nested class, methods of a member class can access instance fields in a containing class.

A nested class must be declared static, and is used to group related classes together. A member class, on the other hand, implies a more intimate relationship, with each instance of the class corresponding to an instance of the enclosing class.

To see how this works, consider an example like:
        public class mem1 {
                private int i = 37;

                class mem11 {}

                public static void main(String args[])
                {
                        mem1 ref1 = new mem1();
                        mem11 ref11 = ref1.new mem11();
                }
        }

mem1 is an ordinary top-level class, and mem11 a member class within it. New instances of mem1 are created in the usual way, but new instances of mem11 require new syntax:
        mem11 ref11 = ref1.new mem11();

That is, an instance of mem11 is being created in the context of a specific enclosing instance of mem1, which is referred to by ref1.

A further example of how member classes are used is illustrated by this code:
        public class mem2 {
                int i = 0;

                class mem22 {
                        int i = 0;
                        void f()
                        {
                                this.i = 37;
                                mem2.this.i = 47;
                                System.out.println(this.i);
                                System.out.println(mem2.this.i);
                        }
                }

                public static void main(String args[])
                {
                        mem2 m2 = new mem2();
                        mem22 m22 = m2.new mem22();
                        m22.f();
                }
        }

We create the new top-level and member class instances, and then call the f() method in the member class. Both the enclosing and member classes have an "i" field, and to access each of these, we say:
        this.i = 37;
        mem2.this.i = 47;

"mem2.this" is the reference to the enclosing instance of mem2.

Member classes are most useful as helper classes to other classes, in situations where the helper class needs to get at the instance variables of the containing class. An example might be some type of a data structure class like a tree, that defines a member class implementing java.util.Enumeration to traverse the structure.


Reference Classes

Java 1.2 adds a new feature for manipulating object references, the Reference class and related support classes. This feature is a little hard to describe, but we could say that "Reference is to object references as Class is to Java classes". That is, a Reference represents an object reference.

To see how this feature works, let's look at an example:
        import java.lang.ref.*;

        public class ref1 {

                public static Reference ref = null;
                public static ReferenceQueue rq = new ReferenceQueue();

                public static void f()
                {
                        Object obj = new Object();
                        System.out.println(obj);
                        ref = new GuardedReference(obj, rq);
                }

                public static void main(String args[])
                {
                        f();

                        try {
                                Reference r = rq.remove(500); // 500ms timeout
                                Object obj = (r == null ? null : r.get());
                                System.out.println(obj);
                        }
                        catch (InterruptedException e) {
                                System.err.println(e);
                        }

                        System.gc();

                        try {
                                Reference r = rq.remove(500);
                                Object obj = (r == null ? null : r.get());
                                System.out.println(obj);
                        }
                        catch (InterruptedException e) {
                                System.err.println(e);
                        }
                }

        }

In this example, main() calls a method f(), and f() creates a local Object instance. We then create a GuardedReference wrapper for this local object, and specify a ReferenceQueue as well. The wrapper has the usual get() and set() methods for obtaining the object (the "referent") that has been set, and for setting a new one.

But there's another aspect of Reference that goes beyond simple support for wrapping a reference in a wrapper class. When we created the GuardedReference object, we also specified a queue. This process is known as "registering" the Reference object.

In this example, when f() returns, the object created locally is garbage, that is, has no valid references to it. At some point the garbage collector will realize this, and will add the Reference object to the specified queue ("enqueue" it).

Why is this feature useful? One example would be a caching mechanism, where objects represent in-memory disk files, and it's important to know when a cached object is no longer in use (so that it can be replaced in the cache by a higher-priority object).

Queues are used to represent Reference objects, so that for example a separate thread can continually check the queue. Queues can also be polled using the mechanism illustrated above (500 millisecond timeout in this example).

There are several types of Reference objects. GuardedReference is one, and WeakReference and PhantomReference two others. These differ in their various properties, for example in whether the no-longer-reachable referent can actually be reclaimed by the garbage collector.

This mechanism can be used for various types of caching and for implementing object cleanup schemes.


Blank Finals

Speaking of the use of final variables, another new feature in 1.1 is the ability to initialize a final class field after its declaration. It used to be that you'd have to say:
        final int BUFSIZ = 1024;

with the initializer in the declaration itself. That restriction has been relaxed, so that a final variable can be initialized via an instance initializer or a constructor. As an example, consider this:
        public class blank {

                final int x;

                public blank(int i)
                {
                        x = i;          // OK
                        x = 47;         // error here
                }

                public blank()          // error here
                {
                }

                public void f()
                {
                        x = 57;         // error here
                }
        }

The final variable in this example needs to be initialized exactly once in each of the constructors, and cannot be initialized in a method such as f().

This feature offers some flexibility when initializing member fields, for example via constructor arguments.


Throwable, Error, Exception and RuntimeException

Java uses exceptions to signal error conditions. Some of these errors are at user level, for example when a data file cannot be accessed. Others originate in the Java runtime system, for example when memory is exhausted or an invalid subscript is applied to an array. There are four different exception classes defined in java.lang that are important to understand when using exceptions.

Throwable is the superclass for all exceptions. If a program catches an exception of type Throwable:
        try {
                ...
        }
        catch (Throwable e) {
                ...
        }

it will catch all exceptions.

Error is a subclass of Throwable, used to group together exceptions that indicate serious problems which a normal application should not try to catch. An example of an exception subclass of Error is VirtualMachineError.

Exception is a subclass of Throwable used to group "normal" kinds of exceptions, such as IOException thrown when an I/O problem occurs.

RuntimeException is a subclass of Exception, and indicates an exception that is not required to be mentioned in a "throws" clause of a method. Exceptions in this category are known as "unchecked" exceptions. For example, if I have a method:
        void f(String fn) throws IOException
        {
                FileOutputStream fos = new FileOutputStream(fn);
                ...
        }

I must either catch any IOException myself, or else declare that f() propagates this exception to its caller. On the other hand, there is no requirement that f() declare that it might throw NullPointerException (which could happen if fn is null). NullPointerException is a subclass of RuntimeException and thus is unchecked.

These classes have constructors that allow the specification of an error message, as in:
        throw new Error("out of memory");

There is also a provision for dumping out a stack traceback:
        try {
                ...
        }
        catch (Error e) {
                e.printStackTrace();
        }


Native Interface Programming

One of the interesting issues that comes up with Java programming is how to combine Java classes with code written in other languages such as C or C++. It's common to have a way to do mixed-language programming on a given platform, and so it's worth asking how this can be done in Java. This is a complex topic, with some system-specific aspects to it. But we will attempt to illustrate some of the basics with a simple example.

Suppose that you have a Java class:
        public class test {

                public static native short f(short a, short b);

                public static void main(String args[])
                {
                        short num1 = 37;
                        short num2 = 47;

                        System.load("test"); // load the DLL

                        short s = f(num1, num2);

                        System.out.println(s);
                }

        }

and you'd like to call a method defined in another language ("native" method). In the above example f() represents such a method.

The first thing to do is declare the method, using the native modifier. A native method has no body, because the body will be provided by some other module written in some other language. Declaring the method in Java allows for type checking and so on to be performed.

We then compile this class:
        javac test.java

The JDK tool "javah" is next run over the class file:
        javah -jni test

The output of this is a file test.h:
        /* DO NOT EDIT THIS FILE - it is machine generated */
        #include <jni.h>
        /* Header for class test */

        #ifndef _Included_test
        #define _Included_test
        #ifdef __cplusplus
        extern "C" {
        #endif
        /*
         * Class:     test
         * Method:    f
         * Signature: (SS)S
         */
        JNIEXPORT jshort JNICALL Java_test_f
          (JNIEnv *, jclass, jshort, jshort);

        #ifdef __cplusplus
        }
        #endif
        #endif

This header describes the prototype for a C++ function to be implemented. We then create test.c to implement the function:
        #include <jni.h>
        #include "test.h"

        JNIEXPORT jshort JNICALL
        Java_test_f(JNIEnv*, jclass, jshort a, jshort b)
        {
                return a * b;
        }

and make a shared library (DLL) out of it by saying (Borland C++ 5.2):
        bcc32 -tWD -DWIN32 -Ij:/java/include -Ij:/java/include/win32 test.c

picking up JNI headers found in j:/java/include and j:/java/include/win32.

Finally, we execute the Java program:
        java test

The program loads the DLL and then calls the f() method within it.

This approach involves a certain amount of magic. The details of creating DLLs, picking up JNI header files, and actually loading shared libraries into a running Java program will vary from system to system. There are also big issues with parameter passing, return values, accessing object instances, and so on. The above example gives the flavor of how JNI works. The basic idea is to declare native methods, use javah to create a header that declares function prototypes for them, implement the prototypes, create a shared library, and then load the library into a Java program.


Narrowing

In Java, the term "narrowing" refers to a conversion from one type to another, a conversion that may lose information. An example of a narrowing conversion would be this:
        public class test1 {
                public static void main(String args[])
                {
                        long a = 123456;
                        short b = (short)a;
                }
        }

and Java allows the long to be converted to a short only via an explicit cast.

The cast rule is suspended in some cases where the value to be assigned is known to the compiler:
        public class test2 {
                public static void main(String args[])
                {
                        short a = 12345;
                }
        }

In this example, "12345" is a constant expression (see 15.27 in the Java Language Specification), and the compiler knows that this value is representable in a short (which supports values -32768 - 32767).

But implicit narrowing is not done on method invocation, so that:
        public class test3 {
                public static void f(short s) {}

                public static void main(String args[])
                {
                        f(0); // error because "0" is an int, not short
                }
        }

is invalid without a cast. This restriction is intended to simplify the overloaded method matching process. In C++, with many more conversion possibilities, argument matching of overloaded functions is very complex.

Narrowing also applies to reference types. For example, if A is a superclass of B, then converting an A reference to a B reference is a narrowing conversion. In this case, the converted reference value is checked as to whether it is actually a legitimate value of type B.

Narrowing is often quite useful. Java provides mechanisms for narrowing values, with the desirable requirement that such narrowing be explicitly identified via casts.


Float and Double

Float and Double are wrapper classes defined in java.lang. They can be used to wrap individual values of float and double types, and insert such values into object collections such as those represented by Vector. These classes are also used to convert floating-point values to and from strings.

But these classes have another purpose, which is to represent properties of floating-point types. For example, they define the constant MAX_VALUE that specifies the maximum float or double value, and POSITIVE_INFINITY to represent an infinite value.

One of the most interesting features of these classes are methods that convert to and from bit representations of floating-point values. For example, this code:
        public class floatbits {

                public static void main(String args[])
                {
                        int bits = Float.floatToIntBits(12.34f);

                        int sign = bits >>> 31;
                        int exp = (bits >>> 23) & 0xff;
                        int mant = bits & 0x7fffff;

                        System.out.println("sign = " + sign);
                        System.out.println("exponent = " + exp);
                        System.out.println("mantissa = " + mant);
                }

        }

picks apart a 32-bit floating-point value, and displays its sign, exponent, and mantissa. Representation of floating-point values uses the IEEE 754 format (see 4.2.3 in the Java Language Specification). In this particular case, the sign is in bit 31, the exponent in bits 30-23, and the mantissa in bits 22-0.

For a 32-bit floating-point value, the bit representation of the value also doubles as the hash code, used for example when inserting a Float object into a Hashtable.


AWT


Delegation

In the last issue we mentioned the "deprecation" of some AWT methods as of the 1.1 release. Some of these methods are isolated, but the basic event handling model of the AWT has itself changed in 1.1, and it's worth illustrating the new model.

The old model was based on inheritance, with subclassing of AWT components and overriding of the action() and handleEvent() methods. An overriding method either would handle events and pass back "true", or else pass back "false" and propagate the event up to the next component in the hierarchy.

This model is somewhat unwieldy, with a requirement for lots of subclasses, complex logic to process events, and lack of separation between application and interface.

As of 1.1, a new "delegation" model has been implemented. In the new model, a "listener" object expresses interest in events from one or more "source" objects. When an event occurs, it is passed from the source to all registered listeners. Since the listener object implements a standard interface (like ActionListener), the source can invoke a known method on the listener object, to pass the details of the event.

To illustrate this further, consider the following example:
        import java.awt.*;
        import java.awt.event.*;

        class Dispatch implements ActionListener {
                static final int COMMAND1 = 1;
                static final int COMMAND2 = 2;
                static final int EXIT = 3;

                int id;
                Delegate d;

                public Dispatch(int id, Delegate d)
                {
                        this.id = id;
                        this.d = d;
                }

                public void actionPerformed(ActionEvent e)
                {
                        switch (id) {
                                case COMMAND1:
                                        d.command1();
                                        break;
                                case COMMAND2:
                                        d.command2();
                                        break;
                                case EXIT:
                                        d.exit();
                                        break;
                        }
                }
        }

        class Interface {
                public Interface(Delegate d)
                {
                        Frame f = new Frame();
                        f.setLayout(new FlowLayout());

                        Dispatch cmd1 = new Dispatch(Dispatch.COMMAND1, d);
                        Dispatch cmd2 = new Dispatch(Dispatch.COMMAND2, d);
                        Dispatch exit = new Dispatch(Dispatch.EXIT, d);

                        Button b = null;

                        b = new Button("Command #1");
                        f.add(b);
                        b.addActionListener(cmd1);

                        b = new Button("Command #2");
                        f.add(b);
                        b.addActionListener(cmd2);

                        b = new Button("Exit");
                        f.add(b);
                        b.addActionListener(exit);

                        f.pack();
                        f.show();
                }
        }

        public class Delegate {
                public void command1()
                {
                        System.out.println("command #1 invoked");
                }

                public void command2()
                {
                        System.out.println("command #2 invoked");
                }

                public void exit()
                {
                        System.exit(0);
                }

                public static void main(String args[])
                {
                        Delegate d = new Delegate();
                        Interface i = new Interface(d);
                }
        }


In this example, Delegate is our application, and Interface the interface for it. Dispatch is a helper class that implements the ActionListener interface. We tie particular object instances of Dispatch to individual buttons. When a button is selected, an event is passed to the listener for the button, and it in turn invokes the appropriate application method.

There is a lot more that can be said about this approach. A paper that describes it in some detail can be found at:

http://www.javasoft.com/products/jdk/1.1/docs/guide/awt/designspec/events.html

We will be using the delegation model in future AWT examples.


Printing

We saw a simple example above of printing text. Another approach to printing uses the AWT:
        import java.awt.*;

        public class print {

                public static void main(String args[])
                {
                        Frame f = new Frame("test");

                        PrintJob pj =
                            f.getToolkit().getPrintJob(null, "print1", null);

                        Graphics g = pj.getGraphics();

                        g.drawLine(0, 0, 150, 150);

                        pj.end();
                }

        }

This example creates a frame, and retrieves its toolkit. The toolkit is the interface between the AWT proper and an actual GUI/window implementation.

From the toolkit we get a PrintJob instance. This causes a print job screen to be popped up. It asks about how many copies are desired and similar types of questions.

Given a PrintJob instance, we can get a Graphics object, and draw a line to it, and then end the print job.

This feature is in 1.1 and 1.1.1, but without any documentation in the source code, so this presentation is a bit sketchy. There are additional method calls for getting page dimensions and resolution.

As compared to the method illustrated above, using device files, the AWT approach to printing offers platform independence (you don't have to worry about pathnames like "/dev/lp"). This approach also offers graphics and Unicode support.

We will likely have more to say about printing in the future.


Applet Serialization

In previous issues we've discussed some of the aspects of applet programming, and also mentioned a new technique for serializing objects, that is, turning an object into a stream of bytes that can be saved to a file or sent across a network.

We can combine these techniques in order to serialize an applet, that is, to capture it in some current state, and then restore that state later. This technique has various uses, for example to perform one-time initialization.

Consider first an applet:
        import java.awt.*;

        public class applet extends java.applet.Applet {

                private int cnt = 1;

                public void paint(Graphics g)
                {
                        String s = "Hello world #" + cnt++;
                        g.drawString(s, 25, 25);
                }

        }

and some HTML that drives it:
        <html>
        <head>
        <title>Applet Example</title>
        </head>
        <body>
        <applet code="applet.class" width=150 height=150></applet>
        </body>
        </html>

If we run this HTML with the JDK 1.1.2 appletviewer program (1.1 gives an exception for what we're going to do), we can Stop and Start the applet a few times, using the appletviewer menu. Each start results in the displayed count being bumped up by one.

Then, if we select Save, with a name like "applet.ser", the applet object is serialized and written to the indicated file. We can start up the appletviewer again with HTML code of:
        <html>
        <head>
        <title>Applet Example</title>
        </head>
        <body>
        <applet object="applet.ser" width=150 height=150></applet>
        </body>
        </html>

and the applet will take up where it left off.

This technique is quite powerful and useful in a variety of applications. But if you are considering using this approach, you should investigate some of the issues around serialization. For example, not all objects can be serialized. One case of this would be machine-specific resources like file descriptors.


Custom Components

In this issue we will use a longish example of a calculator program to illustrate how one would go about designing a custom AWT component using JDK 1.1.

For a calculator program, where there are several buttons representing digits and operators like "+", one approach would be use the Button component in the AWT. But we have chosen to design our own component for this purpose, which consists of a reddish oval with a number or operator inside. This class is called my_Button in the code below.

When we design our own component, there are several important points to note. One is that the component should have a constructor, to create new instances.

Another is that the component needs a paint() method, to draw itself on the screen.

The component should define getPreferredSize() and getMinimumSize(), to declare its size to the AWT. This is important in setting up the bounding box for the component and for doing layout.

A custom component may be a subclass of an AWT class like Canvas, or in 1.1, it may extend java.awt.Component directly. This latter approach results in what is called a "lightweight" component, one without a native peer in the local windowing system. Lightweight components can be more efficient, because of less overhead. my_Button in the example below is lightweight.

Finally, we should mention how event handling is done in this example. Each of the calculator buttons has a single instance of "docalc" registered as a listener for it, so that when the button is selected, docalc.actionPerformed() is called. This is the method that actually does all the calculating work.

How do we know when a button is selected, given that we are doing our own buttons as custom components? For this, we have another level of event handling, where we register a mouse listener for each instance of my_Button. When the mouse is clicked, we detect that, and dispatch in turn to the listener for the selected button. We could have the mouse clicks dispatch straight through to the docalc instance, but such an architecture is not clean. It's desirable that my_Button act like a regular button, handling its selection of itself within itself and dispatching to a registered listener.
        import java.awt.*;
        import java.awt.event.*;

        class my_Button extends Component implements MouseListener {
                private static final Font        f   =  new Font("Monospaced", 12, Font.PLAIN);
                private static final FontMetrics fm  = Toolkit.getDefaultToolkit().getFontMetrics(f);
                private static final int         asc = fm.getLeading() + fm.getMaxAscent();
                private static final int         h   = asc + fm.getMaxDescent();
                private static final int         PAD = 3;

                private String         str    = null;
                private int            w      = 0;
                private ActionListener listen = null;

                public my_Button(String s, ActionListener al)
                {
                        str = s;

                        w = fm.stringWidth(s);
                        listen = al;
                        addMouseListener(this);
                }

                public void mouseEntered(MouseEvent e) {}
                public void mouseExited(MouseEvent e) {}
                public void mousePressed(MouseEvent e) {}
                public void mouseReleased(MouseEvent e) {}

                public void mouseClicked(MouseEvent e)
                {
                        listen.actionPerformed(new ActionEvent(this, 0, str));
                }

                public Dimension getPreferredSize()
                {
                        return new Dimension(w + PAD * 2, h + PAD * 2);
                }

                public Dimension getMinimumSize()
                {
                        return getPreferredSize();
                }

                public void paint(Graphics g)
                {
                        Color c = g.getColor();

                        g.setFont(f);

                        g.setColor(Color.red);
                        g.fillOval(0, 0, w + PAD * 2 - 1, h + PAD * 2 - 1);
                        g.setColor(Color.white);
                        g.drawString(str, PAD, asc + PAD);
                        g.setColor(c);
                }

        }

        class docalc implements ActionListener {

                private String str_accum = "";
                private String str_num   = "";
                private double accum     = 0.0;
                private double num       = 0.0;
                private String oper      = null;

                private void setText(String s)
                {
                        calc.tf.setText(s);
                        calc.tf.setCaretPosition(s.length());
                }

                public void actionPerformed(ActionEvent e)
                {
                        String str = e.getActionCommand();

                        if (str.equals("Exit"))
                                System.exit(0);

                        if (Character.isDigit(str.charAt(0))) {
                                str_num += str;
                                str_accum += str;
                                setText(str_accum);
                        }
                        else {
                                if (str_num.length() >= 1) {
                                        num = Long.parseLong(str_num);
                                        str_num = "";
                                        if (oper != null) {
                                                switch (oper.charAt(0)) {
                                                case '+':   accum += num;      break;
                                                case '-':   accum -= num;      break;
                                                case '*':   accum *= num;      break;
                                                case '/':   accum /= num;      break;
                                                }
                                        }
                                        else {
                                                accum = num;
                                        }
                                        str_accum += str;
                                        setText(str_accum);
                                        oper = str;
                                }
                                if (str.equals("=")) {
                                        String s = Double.toString(accum);
                                        setText(s);
                                        str_accum = "";
                                        accum = 0.0;
                                        oper = null;
                                }
                        }
                }
        }

        public class calc {

                public static TextField tf = null;

                private static my_Button b(String s, docalc dc)
                {
                        return new my_Button(s, dc);
                }

                private static void setup(Frame f, docalc dc)
                {
                        tf = new TextField(25);
                        tf.setEditable(false);
                        tf.setBackground(Color.blue);
                        tf.setForeground(Color.white);

                        GridBagConstraints gbc = new GridBagConstraints();

                        f.setLayout(new GridBagLayout());

                        gbc.insets = new Insets(2, 2, 2, 2);

                        gbc.gridx = 0;
                        gbc.gridy = 0;
                        gbc.gridwidth = 10;
                        gbc.anchor = GridBagConstraints.WEST;
                        f.add(tf, gbc);

                        gbc.gridy = 1;
                        gbc.gridwidth = 1;
                        gbc.anchor = GridBagConstraints.CENTER;

                        gbc.gridx = 0;
                        f.add(b("0", dc), gbc);

                        gbc.gridx = 1;
                        f.add(b("1", dc), gbc);

                        gbc.gridx = 2;
                        f.add(b("2", dc), gbc);

                        gbc.gridx = 3;
                        f.add(b("3", dc), gbc);

                        gbc.gridx = 4;
                        f.add(b("4", dc), gbc);

                        gbc.gridy = 2;

                        gbc.gridx = 0;
                        f.add(b("5", dc), gbc);

                        gbc.gridx = 1;
                        f.add(b("6", dc), gbc);

                        gbc.gridx = 2;
                        f.add(b("7", dc), gbc);

                        gbc.gridx = 3;
                        f.add(b("8", dc), gbc);

                        gbc.gridx = 4;
                        f.add(b("9", dc), gbc);

                        gbc.gridy = 3;

                        gbc.gridx = 0;
                        f.add(b("+", dc), gbc);

                        gbc.gridx = 1;
                        f.add(b("-", dc), gbc);

                        gbc.gridx = 2;
                        f.add(b("*", dc), gbc);

                        gbc.gridx = 3;
                        f.add(b("/", dc), gbc);

                        gbc.gridx = 4;
                        f.add(b("=", dc), gbc);

                        gbc.gridy = 2;
                        gbc.gridx = 6;
                        gbc.weightx = 1;
                        f.add(b("Exit", dc), gbc);
                }

                public static void main(String args[])
                {
                        Frame f = new Frame("calculator");

                        setup(f, new docalc());

                        f.pack();
                        f.show();
                }
        }


ScrollPane

In previous issues we've seen some examples of the use of scrollbars in GUI applications. With Java 1.1, there is an additional technique for performing scrolling within an application. This approach uses the ScrollPane class in the AWT. ScrollPane does "automatic" scrolling of a specified AWT component, with some user customization possible.

For example, with this code:
        import java.awt.*;

        class CirclePanel extends Component {

                public Dimension getPreferredSize()
                {
                        return new Dimension(150, 150);
                }

                public Dimension getMinimumSize()
                {
                        return getPreferredSize();
                }

                public void paint(Graphics g)
                {
                        g.fillOval(5, 5, 125, 125);
                }
        }

        public class scroll {

                public static void main(String args[])
                {
                        Frame f = new Frame("testing");

                        ScrollPane sp = new ScrollPane();
                        sp.setSize(100, 100);

                        sp.add(new CirclePanel());

                        f.add(sp);

                        f.pack();
                        f.setVisible(true);

                        sp.setScrollPosition(65, 65);

                        Adjustable a = sp.getVAdjustable();
                        a.setUnitIncrement(5);
                }

        }

we create our our own component (CirclePanel) with a filled-in circle inside of it, of size 150 x 150. We create a 100 x 100 ScrollPane object and add this component to it. Without any further settings, this will create a scroll pane with scrollbars, and unit increments for the horizontal and vertical scrollbars.

In this particular example, we have modified the default settings in two ways, one to set the current scroll position to (65,65), and the other to set the vertical scroll increment to 5.

There are several other available features for controlling ScrollPane objects.


Popup Menus

A new feature in JDK 1.1 is popup menus, where a menu is popped up over a GUI component in response to a user mouse action. A simple example of this looks like:
        import java.awt.*;
        import java.awt.event.*;

        class MyPopup extends Component implements ActionListener {

                PopupMenu pum = null;

                String labels[] = {"Command 1", "Command 2", "Command 3"};
                String cmds[]   = {"command1", "command2", "command3"};

                public void actionPerformed(ActionEvent e)
                {
                        String cmd = e.getActionCommand();
                        System.out.println("command was: " + cmd);
                }

                public MyPopup()
                {
                        pum = new PopupMenu();

                        for (int i = 0; i < labels.length; i++) {
                                MenuItem mi = new MenuItem(labels[i]);
                                mi.setActionCommand(cmds[i]);
                                mi.addActionListener(this);
                                pum.add(mi);
                        }

                        add(pum);

                        enableEvents(AWTEvent.MOUSE_EVENT_MASK);
                }

                public void processMouseEvent(MouseEvent e)
                {
                        if (e.isPopupTrigger())
                                pum.show(this, e.getX(), e.getY());
                        else
                                super.processMouseEvent(e);
                }

                public Dimension getPreferredSize()
                {
                        return new Dimension(300, 300);
                }

        }

        public class popup {

                public static void main(String args[])
                {
                        Frame f = new Frame("testing");

                        Panel p = new Panel();

                        p.add("Center", new MyPopup());
                        f.add(p);

                        f.pack();
                        f.setVisible(true);
                }
        }

In this example we create a lightweight component of size 300 x 300, and add a popup menu to it. The menu is created by adding MenuItems to it, with separation of menu labels and commands (a rudimentary step toward internationalization).

We also set up to handle mouse events, which is required to catch the event that triggers the popup. The actual event handling is found in processMouseEvent(), where popup events are handled directly, and others are passed on. When actionPerformed() is called, we can retrieve the action command set earlier.


System Colors

SystemColor is a new 1.1 class that supports access to system colors, that is, colors configured by a user for such things as text, window borders, scrollbars, and so on.

For example, this simple program draws a graphical object that is the same color as a window title bar:
        import java.awt.*;

        class MyCanvas extends Canvas {

                public Dimension getPreferredSize()
                {
                        return new Dimension(200, 200);
                }

                public void paint(Graphics g)
                {
                        g.setColor(SystemColor.activeCaption);
                        g.fillOval(50, 50, 100, 100);
                }
        }

        public class color {

                public static void main(String args[])
                {
                        Frame f = new Frame("testing");

                        Panel p = new Panel();
                        p.add(new MyCanvas());

                        f.add(p);

                        f.pack();
                        f.setVisible(true);
                }
        }

The actual RGB values of a system color may change over time, because a user can customize such colors (for example, using Control Panel with Windows NT). The method getRGB() can be applied to a color to obtain its current color settings.


Custom Cursors

We talked earlier in this issue about focus traversal, a new AWT feature in 1.1. Another new 1.1 feature is custom cursors. With 1.0, one could only set a customized cursor at the Frame level, but with 1.1, a cursor may be set for any component. For example, in this code:
        import java.awt.*;
        import java.awt.event.*;

        public class testcursor implements ActionListener {

                public void actionPerformed(ActionEvent e)
                {
                        System.exit(0);
                }

                public static void main(String args[])
                {
                        Frame f = new Frame("testing");
                        f.setLayout(new BorderLayout());

                        Button b = new Button("OK");
                        b.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
                        b.addActionListener(new testcursor());

                        f.add("North", b);

                        f.setSize(200, 200);
                        f.setVisible(true);
                }
        }

we set a crosshair cursor that is effective only within the area of the Button object that we create.


Keyboard Focus Traversal

We've been spending some time discussing some new features of Java 1.1. Another of these is keyboard focus traversal, which is a fancy way of saying that you can use Tab to move between AWT components, giving each the focus in turn.

For example, with this simple program:
        import java.awt.*;

        public class focus {

                public static void main(String args[])
                {
                        Frame f = new Frame("testing");
                        f.setLayout(new FlowLayout());

                        Button b1 = new Button("Button #1");
                        Button b2 = new Button("Button #2");
                        f.add(b1);
                        f.add(b2);

                        f.pack();
                        f.show();
                }
        }

you can move between the two buttons by pressing Tab, if you are using Java 1.1. Tab is ignored for this same program using 1.0.

Components are traversed in the order added, so button #1 will be visited before button #2. No other knowledge or setup is required to use this feature.


List

Una List e' un elemento grafico costituito da una serie di righe all'interno di una area scrollabile (se necessario), consente la selezione di un Item tra quelli selezionabili (e' pero' possibile eseguire anche selezioni multiple).
In altri linguaggi, come Visual Basic, viene definito ListBox.

La classe List e' descritta nella documentazione come:
"Una lista scollabile di voci testuali."

Costruttori

Metodi

Examples

  public void testChoice()
  {
  }


Choice

Un Choice e' un elemento grafico che sostanzialmente assomiglia ad un Menu, consente la selezione di un Item tra quelli selezionabili.
In altri linguaggi, come Visual Basic, viene definito ComboBox.
Cliccando sul tasto a destra del Choice viene presentato l'elenco degli item da cui scegliere il desiderato.
Con un Choice e' possibile scegliere una sola tra piu' scelte.

Examples

  public void testChoice()
  {
    Button bu_ok     = new Button( "   OK   " );
    Choice ch_one    = new Choice();
    Choice ch_two    = new Choice();

    dlg = new DialogBase( form.frame, "testChoice", true );
    dlg.setSize( 400, 300 );
    dlg.setLayout( new FlowLayout() );

    // add listeners
    // bu_ok.addActionListener( this );
    ch_one.addItemListener( this );
    ch_two.addItemListener( this );

    ch_one.setName( "ONE" );          // set name of Choice
    ch_one.add( "1" );                // add items
    ch_one.add( "2" );
    ch_one.add( "3" );

    ch_two.setName( "TWO" );          // set name of Choice
    ch_two.add( "A" );                // add items
    ch_two.add( "B" );
    ch_two.add( "C" );

    // add objects to the Dialog
    dlg.add( ch_one );
    dlg.add( ch_two );
    dlg.add( bu_ok );

    dlg.setVisible( true );
  }

  // manage Choices
  public void itemStateChanged( ItemEvent e )
  {
    if( e.getSource() instanceof Choice )
    {
      String obj  = ((Component)e.getSource()).getName();
      String item = (String) e.getItem();
      if( obj.equals( "ONE" ) )
      {
        if( item.equals( "1" ) ) System.out.println( "(UNO:1)" );
        if( item.equals( "2" ) ) System.out.println( "(UNO:2)" );
        if( item.equals( "3" ) ) System.out.println( "(UNO:3)" );
      }
      else if( obj.equals( "TWO" ) )
      {
        if( item.equals( "A" ) ) System.out.println( "(UNO:A)" );
        if( item.equals( "B" ) ) System.out.println( "(UNO:B)" );
        if( item.equals( "C" ) ) System.out.println( "(UNO:C)" );
      }
    }
  }


Center a Window

  Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
  awindow.move(   ( screen.width  - awindow.size().width  ) / 2,
                  ( screen.height - awindow.size().height ) / 2  );


Example 1

In previous issues we've discussed various aspects of applet programming. Starting with this issue, we're going to branch out a bit and consider the more general topic of constructing Graphical User Interfaces (GUIs) using the AWT (Abstract Windowing Toolkit). The examples we present will be actual Java standalone programs rather than applets. That is, they'll have their own main() method.

The AWT can be viewed as a high-level abstract means of describing what a window or windows should look like. The AWT is actually implemented differently on different platforms, using windowing primitives appropriate to each platform. You can view GUI programming using the AWT as construction of a complex data structure describing a window, which is interpreted at program execution time.

Let's start out by looking at a simple example. This program takes a single argument, which is the name of a text file, and displays that file in a scrollable window. This is kind of like the "more" program available in UNIX and DOS:
        import java.awt.*;
        import java.io.*;

        public class More extends Frame {

                private static Button   b1 = new Button( "New File" );
                private static Button   b2 = new Button( "Exit" );
                private static TextArea ta = new TextArea( 24, 80 );

                // handle mouse events

                public boolean action(Event e, Object arg)
                {
                        if (e.target instanceof Button) {
                                if (e.target == b1) {
                                        new_file();
                                        return true;
                                }
                                if (e.target == b2) {
                                        System.exit(0);
                                        return true;
                                }
                                return false;
                        }
                        else {
                                return false;
                        }
                }

                // select a new file to view

                private void new_file()
                {
                        FileDialog fd = new FileDialog(this, "Open File",
                            FileDialog.LOAD);
                        fd.show();
                        if (fd.getDirectory() == null ||
                            fd.getDirectory().equals(""))
                                return;
                        if (fd.getFile() == null || fd.getFile().equals(""))
                                return;
                        if (fd.getFile().equals("*.*"))
                                return;
                        String fn = fd.getDirectory() + File.separator +
                            fd.getFile();
                        load(fn);
                }

                // load a file

                private void load(String fn)
                {
                        RandomAccessFile raf = null;

                        StringBuffer sb = new StringBuffer();
                        try {
                                String s = null;
                                raf = new RandomAccessFile(fn, "r");
                                while ((s = raf.readLine()) != null) {
                                        sb.append(s);
                                        sb.append('\n');
                                }
                                raf.close();
                        }
                        catch (Throwable e) {
                                System.err.println("file I/O error");
                                System.exit(1);
                        }
                        ta.setText(sb.toString());
                }

                // constructor

                public More(String title, String fn)
                {
                        super(title);

                        resize(600, 450);

                        Panel p1 = new Panel();
                        p1.setLayout(new FlowLayout( FlowLayout.CENTER, 100, 0) );
                        p1.add(b1);
                        p1.add(b2);

                        Panel p2 = new Panel();
                        ta.setEditable(false);
                        p2.add(ta);

                        setLayout(new FlowLayout());

                        add(p1);
                        add(p2);

                        load(fn);

                        show();
                }

                public static void main(String args[])
                {
                        Frame f = new More( "More", args[0] );
                }

        }

We start by constructing a Frame, an AWT type suitable for representing a top-level application window. We resize the frame, and then add a couple of panels to it. What are panels? A panel is an AWT entity useful for holding or representing a logical chunk of a larger interface. In this example, one of the panels holds a couple of buttons used for switching files and for exiting, and the other holds the text area that actually displays the file.

Given two buttons within a panel, or two panels in a frame, how does the AWT know how to order or arrange these items? We tell the AWT about this via a layout manager, in this case by establishing a FlowLayout object and attaching it to the frame. This type of layout arranges objects in rows left to right, and goes to the next row when it runs out of space. So the two buttons are arranged left to right within panel p1. Panel p2 is laid out on the next "row" after p1, because you can't fit a large text area right next to a set of buttons.

Actual file loading is straightforward. We read the lines in from the file, append them together, and when done call setText() to set the text for the TextArea object. A TextArea object already has scroll bars with it and we don't need to worry about those.

If we want to switch files, we can use a FileDialog entity, which handles most of the work of file selection. This brings up a menu of files that looks similar to what you see on a PC running Windows.

Finally, we define an action() method for handling events in the frame, in this case file switching or exiting.

This program has a couple of deficiencies, most notably that it reads a whole file in at one time. With fairly slow I/O this can be a problem for very large files. But the program does serve to illustrate some of the basics of AWT use. We'll be looking at some of these areas in more detail in future issues.


Example 2

In the last issue we started a series on AWT (Abstract Window Toolkit) programming. We presented an example of writing a "more" program, one that can be used to page through text from a file. In this issue we'll show another more complicated and powerful way of achieving the same end.

Before diving into this, a few general comments are in order. This code is written against JDK 1.1. It gives some "deprecation" warnings, about methods which have changed in the JDK. As such, the code needs to be updated, but doing so would make it not work with earlier versions. This is simply something that we have to put up with in an evolving language.

Also, this code reveals a bug in the JDK 1.1 implementation for Windows NT. If you compile this program and run it, you will notice that scroll bars are flaky. This is a known bug that is supposed to be fixed in a bug fix release of 1.1.

Finally, we are getting into an area that is perhaps not as stable as some other parts of Java, and still evolving (as is my understanding of it). Some aspects of the example below, notably layout manager usage, are a bit tricky.

As you will recall, the previous version of the "more" program had one big deficiency, namely, that it read in a whole file before displaying it. This is not acceptable for a very large file, especially with relatively slow I/O. We've fixed that problem, and to do so, set up our own text windowing scheme with scroll bars which we manage.

Here is the actual code. If you scan down the left side, you can see interspersed comments starting with "//" in the left margin.
        import java.awt.*;
        import java.io.*;

        // a text area in the window

        // This is the class used to manage a text area, with scroll bars.
        // It is passed a RandomAccessFile object to read lines from, and
        // reads them on demand only.
        // A Canvas is an AWT object suitable for displaying text on.

        class Text extends Canvas {

                private static final int LISTSIZ = 16;
                private String           list[]  = null;
                private int              scnt    = 0;
                private int              currpos = 0;
                private RandomAccessFile raf     = null;
                private boolean          ateof   = false;

                // initialize
                void init(RandomAccessFile r)
                {
                        if (raf != null) {
                                try {
                                        raf.close();
                                }
                                catch (Throwable e) {
                                        System.err.println("close err");
                                        System.exit(1);
                                }
                        }
                        raf = r;
                        ateof = false;
                        currpos = 0;

                        scnt = 0;
                        list = new String[LISTSIZ];
                }

        		// We need to detab Strings that are displayed, because tabs
        		// are handled in a funny way by Graphics.drawString().
                private static String detab(String s)
                {
                        StringBuffer sb = new StringBuffer();
                        int len = s.length();
                        int pos = 0;
                        for (int i = 0; i < len; i++) {
                                char c = s.charAt(i);
                                if (c == '\r' || c == '\n') {
                                }
                                else if (c == '\t') {
                                        do {
                                                sb.append(' ');
                                                pos++;
                                        } while (pos % 8 != 0);
                                }
                                else {
                                        sb.append(c);
                                        pos++;
                                }
                        }
                        return sb.toString();
                }

        		// This is the internal list used to store lines to be displayed.
        		// We could also use java.util.Vector to manage this list, and
        		// System.arraycopy() to copy the list.
                // add a String to the list
                void add(String s)
                {
                        if (scnt == list.length) {
                                String x[] = new String[list.length * 3 / 2];
                                for (int i = 0; i < scnt; i++)
                                        x[i] = list[i];
                                list = x;
                        }
                        list[scnt++] = detab(s);
                }

        		// Called by the handleEvent() method below.
                // scroll up or down a line
                void scroll(int dir)
                {
                        currpos += dir;

                        if (currpos < 0)
                                currpos = 0;

                        repaint();
                }

        		// This is the paint() method, used to draw lines in the text window.
        		// We use FontMetrics to determine how tall a character is, and
        		// display as many lines as will fit.
                public void paint(Graphics g)
                {
                        Dimension d = size();
                        int startpos = More.HEIGHT - d.height;
                        int pos = startpos;
                        int sp = g.getFontMetrics().getHeight();
                        int i = currpos;

                        while (pos + sp < d.height) {
                                while (i >= scnt && !ateof) {
                                        String s = null;
                                        try {
                                                s = raf.readLine();
                                        }
                                        catch (Throwable e) {
                                                System.err.println("I/O err");
                                                System.exit(1);
                                        }
                                        if (s == null)
                                                ateof = true;
                                        else
                                                add(s);
                                }
                                if (i >= scnt)
                                        break;
                                g.drawString(list[i], 5, pos);
                                i++;
                                pos += sp;
                        }
                }

        }

        // We use a Panel to contain the text area, along with scroll bars.
        // A Panel is an AWT object that can contain other objects.
        // a Panel for the text area, with scroll bars

        class Panel_ta extends Panel {

                private Scrollbar vbar = null;
                private Text t = null;

                // constructor
                Panel_ta(Text ta)
                {
                        vbar = new Scrollbar(Scrollbar.VERTICAL);

                        // Put the text area in the center, and the scroll bar on the "East" side.
                        setLayout(new BorderLayout(0, 0));
                        add("Center", ta);
                        add("East", vbar);
                        t = ta;
                }

                // handleEvent() is called for actual scrolling.
                public boolean handleEvent(Event e)
                {
                        if (e.target == vbar) {
                                switch (e.id) {
                                        case Event.SCROLL_LINE_UP:
                                                t.scroll(-1);
                                                break;
                                        case Event.SCROLL_LINE_DOWN:
                                                t.scroll(1);
                                                break;
                                }
                                return true;
                        }
                        else {
                                return super.handleEvent(e);
                        }
                }

        }

        // The actual "More" class.

        public class More extends Frame {

                private Button b1 = new Button("New File");
                private Button b2 = new Button("Exit");
                private Text   ta = new Text();

                static final int WIDTH  = 600;
                static final int HEIGHT = 450;

                // A dialog invoked when the user selects "New File".
                // open a new file
                private void new_file()
                {
                        FileDialog fd = new FileDialog(this, "Open File",
                            FileDialog.LOAD);
                        fd.show();
                        if (fd.getDirectory() == null ||
                            fd.getDirectory().equals(""))
                                return;
                        if (fd.getFile() == null || fd.getFile().equals(""))
                                return;
                        if (fd.getFile().equals("*.*"))
                                return;
                        String fn = fd.getDirectory() + File.separator +
                            fd.getFile();
                        load(fn);
                }

                // Called when buttons are selected.
                // handle an action
                public boolean action(Event e, Object arg)
                {
                        if (e.target instanceof Button) {
                                if (e.target == b1) {
                                        new_file();
                                        return true;
                                }
                                if (e.target == b2) {
                                        dispose();
                                        System.exit(0);
                                        return true;
                                }
                                return false;
                        }
                        else {
                                return false;
                        }
                }

                // Load a new file in.
                private void load(String fn)
                {
                        try {
                                RandomAccessFile raf =
                                    new RandomAccessFile(fn, "r");
                                ta.init(raf);
                        }
                        catch (Throwable e) {
                                System.err.println("open err");
                                System.exit(1);
                        }

                        ta.repaint();
                }

                // Constructor for More.
                public More(String title, String fn)
                {
                        super(title);

                        resize(WIDTH, HEIGHT);

        // Set a layout manager for the buttons, flowing across the screen.
                        Panel p1 = new Panel();
                        p1.setLayout(new FlowLayout(FlowLayout.CENTER,50,0));

                        p1.add(b1);
                        p1.add(b2);

        // Set a fixed-width font, size 12.
                        ta.setFont(new Font("Courier", Font.PLAIN, 12));

                        Panel p2 = new Panel_ta(ta);

        // Set a layout manager for the buttons at the top, and the text area
        // at the bottom.  GridBagLayout is the most complicated and powerful
        // of the layout managers.  You can specify cell positions, cell
        // weights, and so on.  Each object to be laid out has its constraints
        // set for it.
        // In this case, we give the top row of buttons a weight of 1, and the
        // text area a weight of 7, so the text area will take 7/8 of the
        // total window.

                        GridBagLayout      gbl = new GridBagLayout();
                        GridBagConstraints gbc = new GridBagConstraints();

                        setLayout(gbl);

                        gbc.gridx   = 0;
                        gbc.gridy   = 0;
                        gbc.weightx = 1;
                        gbc.weighty = 1;
                        gbc.fill    = GridBagConstraints.BOTH;
                        gbc.anchor  = GridBagConstraints.CENTER;

                        gbl.setConstraints(p1, gbc);

                        gbc.gridy = 1;
                        gbc.weighty = 7;

                        gbl.setConstraints(p2, gbc);

                        add(p1);
                        add(p2);

                        load(fn);

                        show();
                }

                // Driver main() method.
                public static void main(String args[])
                {
                        Frame f = new More("More", args[0]);
                }
        }

This example is fairly complicated, but illustrates several important points about the AWT. There are other ways to approach this problem. Another feature we could add would be one to search through the file for a specified string, and display the line it's found on.


Applets and Applications

La differenza tra un Applet e' una Applicazione e' data dalla differente dichiarazione della classe principale.
Nel caso di un Applet, si prevede che sia attivato all'interno di un browser del linguaggio html, come tale necessita della seguente dichiarativa:
public class XXX extends Applet
{
  public void init() {
    ...
  }
}

La prima riga indichera' al browser che la classe dovra' ereditare le caratteristiche della classe Applet.
Nel caso di una Applicazione deve esistere il metodo main perche' la JVM sappia come attivare l'istanza del programma:
public class XXX
{
  public static void main( String args[] ) {
    xxx  =  new XXX();
    ...
  }
}

Examples

public class XXX extends Applet
{
  static    XXX      xxx;
  GUI       gui;

  //----------------------------------------------------------------------------
  // main for start as applet
  public void init() {
    gui  =  new GUI();
  }

  //----------------------------------------------------------------------------
  // main for start as standalone application
  public static void main( String args[] ) {
    xxx  =  new XXX();
    xxx.init();
  }
}



Interfacing to an Applet

Suppose that we want to take the above calculator program and call it from an applet. How would we do this? Here's a simple example of an applet that will interface with the calculator code.
        import java.awt.*;

        public class applet extends java.applet.Applet {
                public void paint(Graphics g)
                {
                        String input_expr = getParameter("input_expr");
                        calc c = new calc(input_expr);
                        String out = c.get_value();
                        g.drawString("Input = " + input_expr, 25, 50);
                        g.drawString("Value = " + out, 25, 75);
                }
        }

This is similar to the applet illustrated in the last issue, save for the lines:
        String input_expr = getParameter("input_expr");
        calc c = new calc(input_expr);
        String out = c.get_value();

The last two of these we saw in the example above. The first line illustrates how one can get parameters passed to the applet from HTML code, kind of similar to command-line parameters. The corresponding HTML to run this applet would be:
        <html>
        <head>
        <title>Interface to Calculator Applet
        </title>
        </head>
        <body>

        <applet code="applet.class" width=150 height=150>
        <param name=input_expr value="1/2/3*4">
        </applet>

        </body>

        </html>

This HTML is similar to that illustrated in newsletter #001, save for the line:
        <param name=input_expr value="1/2/3*4">

which actually passes in the parameter value. When this applet is executed, the result will be something like:
        Input = 1/2/3*4
        Value = 0.666667


Button Input

In the last issue we presented a simple drawing applet, that tracks mouse input and draws on the screen accordingly. Suppose that we'd like to modify this applet so that the user can toggle between two different colors. How might we do this? One approach looks like so:
        import java.applet.*;
        import java.awt.*;

        public class draw extends Applet {

                int prev_x = 0;
                int prev_y = 0;
                boolean color = false;
                Button ccb;

                public void init()
                {
                        ccb = new Button("Toggle Color");
                        add(ccb);
                }

                public boolean action(Event e, Object o)
                {
                        if (e.target == ccb) {
                                color = !color;
                                return true;
                        }
                        else {
                                return false;
                        }
                }

                public boolean mouseDown(Event e, int x, int y)
                {
                        prev_x = x;
                        prev_y = y;

                        return true;
                }

                public boolean mouseDrag(Event e, int x, int y)
                {
                        Graphics g = getGraphics();

                        g.setColor(color == false ? Color.red : Color.blue);

                        g.drawLine(prev_x, prev_y, x, y);

                        prev_x = x;
                        prev_y = y;

                        return true;
                }
        }

using this HTML interface to the applet:
        <html>
        <head>
        <title>Interface to Text Applet
        </title>
        </head>
        <body>
        <applet code="draw.class" width=300 height=300>
        </applet>
        </body>
        </html>

This code is similar to the previous example, but we've added a button that has "Toggle Color" in it. The action() method is called in response to user events, and we check whether the user in fact clicked on the button. If so, we toggle the color state internally, and the color switches from red to blue or vice versa. If the event passed to action() is not a button click, then we pass it back to the parent to handle.

In the next issue we'll get into the area of text input, showing a low-level and a higher-level way of entering text.


Text Input

In the last issue we talked about button input, where the user clicks on a button. In this issue we'll continue our discussion of input and discuss text input a bit, starting with low-level character input. To illustrate what such input looks like, here is a simple applet:
        import java.applet.*;
        import java.awt.*;

        public class text1 extends Applet {

                int x = 40;
                int y = 40;

                public boolean keyUp(Event e, int key)
                {
                        if ((e.modifiers & Event.CTRL_MASK) != 0) {
                                char buf[] = new char[2];
                                buf[0] = '^';
                                key += 0x40;
                                buf[1] = (char)key;
                                getGraphics().drawChars(buf, 0, 2, x, y);
                                x += getFontMetrics(getFont()).charWidth('^');
                                x += getFontMetrics(getFont()).charWidth(key);
                                x += 2;
                        }
                        else {
                                char buf[] = new char[1];
                                buf[0] = (char)key;
                                getGraphics().drawChars(buf, 0, 1, x, y);
                                x += getFontMetrics(getFont()).charWidth(key);
                                x++;
                        }
                        return true;
                }
        }


and the HTML that drives it:
        <html>
        <head>
        <title>Interface to Text Applet</title>
        </head>
        <body>
        <applet code="text1.class" width=350 height=125></applet>
        </body>
        </html>

keyUp() is a method called in response to a keyboard event. Once inside keyUp(), we go through some machinations to determine what sort of a key was pressed, for example whether a control key was hit. The code in this example is incomplete but illustrative of the technique.

Once we have a key, we want to echo it in the applet window. We use drawChars() for this. Then we need to update the X position in the window for drawing the next character, and to do that, we use the FontMetrics class with the current font, to determine the width of a character that's just been drawn.

In other words, this applet simply echoes its input to the window, and throws in a "^" in front of control characters.

There are higher-level methods for entering text, which we will look at in future issues.


Text Fields

In the previous issue we showed how to use low-level character input within an applet. In this issue we'll discuss higher-level input using TextFields. Consider the following example, an applet that gathers name and address from a user and writes the data to a local file:
        import java.applet.*;
        import java.awt.*;
        import java.io.*;

        public class text4 extends Applet {

                TextField tf1;                  // text fields for address
                TextField tf2;
                TextField tf3;
                Button db;                      // Done button
                Label lb;                       // Label used as message
                Color bg = new Color(0xffffff); // background/foreground clrs
                Color fg = new Color(0x000000);

                public void init()
                {
                        // set colors

                        setForeground(fg);
                        setBackground(bg);

                        // create text fields

                        tf1 = new TextField(35);
                        tf2 = new TextField(35);
                        tf3 = new TextField(35);

                        // create button and label

                        db = new Button("Done");
                        lb = new Label();

                        // arrange items in window

                        setLayout(new GridLayout(4, 2, 10, 10));
                        add(new Label("Name:"));
                        add(tf1);
                        add(new Label("Address #1:"));
                        add(tf2);
                        add(new Label("Address #2:"));
                        add(tf3);
                        add(db);
                        add(lb);
                }

                private boolean write()
                {
                        // check input fields

                        if (tf1.getText() == null ||
                            tf1.getText().length() == 0)
                                return false;
                        if (tf2.getText() == null ||
                            tf2.getText().length() == 0)
                                return false;
                        if (tf3.getText() == null ||
                            tf3.getText().length() == 0)
                                return false;

                        // write out name and address to end of file

                        try {
                                RandomAccessFile raf = new
                                    RandomAccessFile("datafile", "rw");
                                raf.seek(raf.length());
                                raf.writeBytes(tf1.getText());
                                raf.writeByte('\n');
                                raf.writeBytes(tf2.getText());
                                raf.writeByte('\n');
                                raf.writeBytes(tf3.getText());
                                raf.writeByte('\n');
                                raf.writeByte('\n');
                                raf.close();
                        }
                        catch (Throwable e) {
                        }

                        return true;
                }

                public boolean action(Event e, Object o)
                {

                        // Done button selected

                        if (e.target == db) {
                                if (write())
                                        lb.setText("Written ...");
                                else
                                        lb.setText("Invalid field value");
                                return true;
                        }
                        else {
                                return false;
                        }
                }
        }

This applet uses the following HTML code to drive it:
        <html>
        <body bgcolor="#ffffff" text="#000000" link="#ff0000" vlink="#000000">
        <head>
        <title>Interface to Text Applet</title>
        </head>
        <body>
        Please enter your name and address then select Done.
        <br>
        <br>
        <applet code="text4.class" width=350 height=125></applet>
        </body>
        </html>

In this example, we use the init() method of the applet to set the background and foreground colors, and then we create the individual items of the window. We use a layout manager for this purpose. A layout manager is a mechanism for arranging items in a window, or more precisely, Component objects in a Container. Applet is derived from Panel which is derived from Container. And an item like TextField is derived from TextComponent which is derived from Component.

So when we create text fields and buttons and so on, we are creating objects of classes derived from Component, and using a layout manager to arrange these objects in a Container object.

After we've set up the window items, we can then add some logic within the action() method to check whether the user selected the Done button, and if so, then we interrogate the text fields using getText() and extract the input data. Finally, we write this data to a file. We append to the end of the file by obtaining its length and positioning at the end of file.


Animation

In previous issues we've talked about the use of basic graphics in a Java applet, and considered some techniques for the input of text. In this issue we'll talk about something quite different, namely animation. This is a technique that can be used to make applets do "neat" things.

First of all, let's look at the source for an animation applet:
        import java.applet.*;
        import java.awt.*;

        public class Animate extends Applet implements Runnable {

                // thread identifier
                private Thread an_thread = null;

                // Start and Stop buttons
                private Button start_button = null;
                private Button stop_button = null;

                // current state of animation
                private int st = 0;

                // initialize the applet
                public void init()
                {
                        start_button = new Button("Start");
                        stop_button = new Button("Stop");
                        add(start_button);
                        add(stop_button);
                }

                // handle mouse actions
                public boolean action(Event e, Object arg)
                {
                        if (e.target == start_button) {
                                start();
                                return true;
                        }
                        else if (e.target == stop_button) {
                                stop();
                                return true;
                        }
                        else {
                                return super.action(e, arg);
                        }
                }

                // start the applet
                public void start()
                {
                        if (an_thread == null) {
                                an_thread = new Thread(this);
                                an_thread.start();
                        }
                }

                // stop the applet
                public void stop()
                {
                        if (an_thread != null && an_thread.isAlive())
                                an_thread.stop();
                        an_thread = null;
                        st = 3;
                }

                // run a thread
                public void run()
                {
                        for (;;) {

                                // get graphics for the applet window

                                Graphics g = this.getGraphics();
                                try {
                                        switch (st++) {

                                        case 0:
                                        g.setColor(Color.red);
                                        g.fillOval(25, 35, 250, 250);
                                        break;

                                        case 1:
                                        g.setColor(Color.yellow);
                                        g.fillOval(125, 135, 150, 150);
                                        break;

                                        case 2:
                                        g.setColor(Color.blue);
                                        g.fillOval(225, 235, 50, 50);
                                        break;

                                        case 3:
                                        g.clearRect(0, 0, 300, 300);
                                        st = 0;
                                        break;

                                        }

                                        // sleep for a second
                                        Thread.sleep(1000);
                                }

                                catch (InterruptedException e) {
                                }
		                        g.dispose();
                }
        }

along with the HTML code that drives the applet:
        <html>
        <head>
        <title>Interface to Animation Applet</title>
        </head>
        <body>
        <applet code="Animate.class" width=300 height=300></applet>
        </body>
        </html>

We've seen parts of the applet code before, for example the graphics that draws ovals (actually circles in this example), the init() method, the setting up of buttons, and so on. This particular animation draws three circles of different sizes and colors, and then repeats itself.

What's different in this example is the use of threads. A thread, which also is called names like "lightweight process" and "task", is a distinct execution of Java code that is taking place at a given time. A program or applet may use threads to allow multiple streams of code to execute simultaneously.

Normally a Java applet responds to events such as mouse clicks or keyboard input. But we'd like this animation applet to do something continuously without waiting for events, so we create a thread and start it running. The lines:
        an_thread = new Thread(this);
        an_thread.start();

create a thread, specifying a reference for a class object that implements the Runnable interface (that is, defines a method run()). This method is executed as the "body" of the thread. In the case at hand, the method simply checks the current state and draws the appropriate circle or else clears the window in preparation for starting another cycle.

We've set up a couple of Start and Stop buttons, tied to start() and stop() methods in the applet. If Stop is selected, the thread execution stops, and Start will restart the animation by reestablishing the thread.

Threads are an important part of Java about which we will say more in future issues.


Client/Server Example

In this issue we'll show a simple example of client/server programming. The client will be an applet and the server a regular Java program with a main() method in it.

In the client/server model, there is a server program running on some computer. It accepts connections from clients across the network, and serves each of them. For example, the client may be used to accept some input from a user, which is sent to the server and entered into a database. The server can also send information back to the client, such as an acknowledgment.

Server and client communicate via what is known as a socket. More technically, a socket is an endpoint of a communications channel, and thus there is a socket on each end of the channel, one socket for the server and one for the client.

Given a socket, it's possible to do conventional Java I/O between server and client.

Let's now look at the programs:
        // server.java

        import java.io.*;
        import java.net.*;

        public class server extends Thread {

                public static final int DEF_PORT = 1234;// port
                private int port;
                private ServerSocket listen;            // server socket

                public server()
                {
                        // set up the server socket port

                        port = DEF_PORT;
                        try {
                                listen = new ServerSocket(port);
                        }
                        catch (IOException e) {
                                System.err.println("socket creation error");
                                System.exit(1);
                        }

                        // start the server thread running

                        start();
                }

                public void run()
                {
                        try {
                                // accept connections and process them

                                for (;;) {
                                        Socket cs = listen.accept();
                                        connection c = new connection(cs);
                                }
                        }
                        catch (IOException e) {
                                System.err.println("connection error");
                        }
                }

                public static void main(String[] args)
                {
                        new server();
                }
        }

        class connection extends Thread {

                private Socket client;                  // client socket
                private DataInputStream in;             // input from socket
                private PrintStream out;                // output to socket

                public connection(Socket cs)
                {
                        // set up an individual connection

                        try {
                                client = cs;
                                InputStream is = client.getInputStream();
                                in = new DataInputStream(is);
                                OutputStream os = client.getOutputStream();
                                out = new PrintStream(os);
                        }
                        catch (IOException e) {
                                try {
                                        client.close();
                                }
                                catch (IOException ee) {
                                        System.err.println("close error");
                                }
                                System.err.println("socket stream error");

                                return;
                        }

                        // start it running
                        start();
                }

                public void run()
                {

                        // read from socket input and write back to output
                        try {
                                for (;;) {
                                        String ln = in.readLine();
                                        if (ln == null)
                                                break;
                                        if (ln.length() == 0)
                                                out.println("empty input");
                                        else
                                                out.println("OK: " + ln);
                                }
                        }
                        catch (IOException e) {
                                System.err.println("server I/O error");
                        }

                        // close connection

                        finally {
                                try {
                                        client.close();
                                }
                                catch (IOException ee) {
                                        System.err.println("close error");
                                }
                        }
                }
        }

        // client.java

        import java.applet.*;
        import java.awt.*;
        import java.io.*;
        import java.net.*;

        public class client extends Applet {

                public static final int DEF_PORT    = 1234;  // port
                Socket                  s;                   // socket
                DataInputStream         in;                  // socket input
                private PrintStream     out;                 // socket output
                TextField               input_field;         // input field
                TextArea                out_area;            // output display area

                public void init()
                {
                        try {
                                // set up socket
                                String host = getCodeBase().getHost();
                                s           = new Socket(host, DEF_PORT);
                                in          = new DataInputStream(s.getInputStream());
                                out         = new PrintStream(s.getOutputStream());

                                // set up window
                                input_field = new TextField();
                                out_area = new TextArea();
                                out_area.setEditable(false);
                                setLayout(new BorderLayout());
                                add("North", input_field);
                                add("Center", out_area);
                        }
                        catch (IOException e) {
                                System.err.println("exception during setup");
                        }
                }

                public boolean action(Event e, Object o)
                {
                        if (e.target == input_field) {

                                // we have some input from the user

                                try {

                                        // dump it to the server

                                        out.println((String)e.arg);
                                        input_field.setText("");

                                        // read response


                                        String status = in.readLine();
                                        out_area.setText(status);

                                        return true;
                                }
                                catch (IOException ee) {
                                        out_area.setText("server I/O error");
                                }
                        }
                        return false;
                }
        }

The server is compiled as usual and simply started as a Java program:
        $ java server

while the client is driven via some HTML:
        <html>
        <head>
        <title>Client Applet</title>
        </head>
        <body>
        <applet code="client.class" width=300 height=300></applet>
        </body>
        </html>

If you start the client without the server being present, it will give an error because of failure to connect to the server.

This particular server/client combination simply validates input from the client and sends back an acknowledgment. Each client connection runs as a separate thread, so that many clients can be served simultaneously without the need for polling.

Note that the server knows to close a given client connection (the client has gone away) by receipt of a "null" when reading input from that connection. By contrast, the server "never" goes away; it must be explicitly terminated by the user.

Note also that the socket port must be unique on the system running the server, and that the server and client must agree on port assignments.


Image Loading and Filtering

An interesting aspect of applet programming that we've not talked much about is the use of images. In this issue we'll present a simple example of how images are loaded and manipulated.

Let's first look at some actual applet code:
        // Filter.java
        import java.applet.*;
        import java.awt.*;
        import java.awt.image.*;

        public class Filter extends Applet {

                Image img;
                Image black;
                boolean flag = true;

                public void init()
                {
                        img = getImage(getDocumentBase(), "image.gif");
                        ImageFilter f = new BlackFilter();
                        ImageProducer ip = new FilteredImageSource(
                            img.getSource(), f);
                        black = createImage(ip);
                        repaint();
                }

                public void update(Graphics g)
                {
                        g.clearRect(0, 0, size().width, size().height);
                        g.drawImage(flag ? img : black, 10, 10, this);
                        flag = !flag;
                }

                public boolean mouseUp(Event e, int x, int y)
                {
                        repaint();
                        return true;
                }
        }

        class BlackFilter extends RGBImageFilter {

                public BlackFilter()
                {
                        canFilterIndexColorModel = true;
                }

                public int filterRGB(int x, int y, int rgb)
                {
        				int a = rgb & 0xff000000;
        				int r = (((rgb & 0xff0000) + 0xff0000) / 2) & 0xff0000;
        				int g = (((rgb & 0xff00) + 0xff00) / 2) & 0xff00;
        				int b = (((rgb & 0xff) + 0xff) / 2) & 0xff;
                        //return a | r | g | b;
                        return a | 0 | 0 | 0;
                }
        }

which is driven by HTML code:
        <html>
        <head>
        <title>Interface to Filter Applet</title>
        </head>
        <body>
        <applet code="Filter.class" width=300 height=300></applet>
        </body>
        </html>

"image.gif" is not supplied in this newsletter and you'll need to find your own image if you want to try this example.

This particular applet draws an image, and then toggles the image to black and back to its original color at each mouse click. The commented-out line of code at the bottom of the applet would instead "gray out" the image by averaging its RGB (red/green/blue) color values with white.

The applet is conventional in structure save for the image filtering and the color manipulation.

The image is retrieved using getImage(), specifying a name ("image.gif") and a URL document base. We then create a black filter and filter the loaded image through it. With image filtering, an ImageProducer is a source of an image. A filter can be applied to the ImageProducer, resulting in another ImageProducer from which an actual image can be created using createImage(). This is a bit of simplification of what the java.awt.image package is really about, but it's sufficient for our purposes.

The RGB transformation averages each color with white, if graying out is desired, or else simply returns 0 | 0 | 0 (black in the RGB color scheme). The high 8 bits (mask 0xff000000) represent the "alpha" value, used to represent transparency.

An example of another type of transformation on images would be CropImageFilter(), that crops an image to a specified rectangle.


Various


Size

Use javac -O. This inlines functions (which makes the bytecode bigger) and removes
            line numbers (which makes it smaller). Unless you use lots of inlinable functions, the
            removal of line numbers will dominate and your bytecode will get smaller. However, note
            that things can sometimes be inlined when they shouldn't -- see the compilers page for
            details.


Memory Usage

In issue #027 we looked at a new 1.2 feature, reference classes. These are used in areas such as caches and garbage collection, as an aid in managing memory.

Another such aid is memory usage advisement. This feature is used to inform an application about where it stands with its use of memory, for example whether it is close to exhausting memory. In such a case the application might for example choose to flush some of its caches, freeing up memory.

To see how this works, consider the following example:
        class Rec {
                double data1;
                double data2;
                double data3;
                double data4;
                double data5;
                double data6;
                double data7;
                double data8;
                double data9;
                double data10;
                Rec next;
        }

        public class mem {
                private static int last_adv = -1;

                public static void getmem()
                {
                        Runtime rt = Runtime.getRuntime();
                        int adv = rt.getMemoryAdvice();

                        if (adv == last_adv)
                                return;
                        last_adv = adv;

                        switch (adv) {
                                case Runtime.MemoryAdvice.GREEN:
                                        System.out.println("green");
                                        break;
                                case Runtime.MemoryAdvice.YELLOW:
                                        System.out.println("yellow");
                                        break;
                                case Runtime.MemoryAdvice.ORANGE:
                                        System.out.println("orange");
                                        break;
                                case Runtime.MemoryAdvice.RED:
                                        System.out.println("red");
                                        break;
                        }
                }

                public static void main(String args[])
                {
                        Runtime rt = Runtime.getRuntime();

                        Rec head = null;
                        for (;;) {
                                Rec r = new Rec();
                                r.next = head;
                                head = r;
                                getmem();
                        }
                }
        }

Rec is a data record of some type, and main() allocates a long linked list of these records. They cannot be garbage collected because all are active (head points at the first, the first points at the second, and so on).

At each iteration of the loop, getmem() is called to check the current status of memory. When the program is run using JDK 1.2 beta 3, output is:
        green
        orange
        red
        java.lang.OutOfMemoryError
                at java.io.FileOutputStream.write(Compiled Code)

The returned advice on memory goes through increasingly serious stages (green, yellow, orange, red), with the latter stages indicating that the application had better do something to free up some of its memory. Java uses garbage collection rather than explicit freeing of memory, and to free memory implies removing references from no-longer-used objects, so that the garbage collector can free them.


Exceptions as a control structure

Java supports the use of exceptions as an integral part of the language. When an invalid condition is detected, an exception is thrown, and later caught by an exception handler (or else the program aborts).

Exceptions can be used for more than error handling, however, and it's interesting to consider their use in improving performance (see also the March 1998 issue of Byte magazine for an article on this topic).

Suppose that you want to add up all the values of the elements in an array. One obvious, and one not-so-obvious, approach to doing this is as follows:
        public class test {
                public static int sum1(int vec[])
                {
                        int s = 0;
                        int len = vec.length;
                        for (int i = 0; i < len; i++)
                                s += vec[i];
                        return s;
                }

                public static int sum2(int vec[])
                {
                        int s = 0;
                        try {
                                for (int i = 0; ;i++)
                                        s += vec[i];
                        }
                        catch (ArrayIndexOutOfBoundsException e) {
                        }
                        return s;
                }

                public static void main(String args[])
                {
                        int x[] = new int[1000000];
                        long t = 0;

                        t = System.currentTimeMillis();
                        int s1 = 0;
                        for (int i = 1; i <= 10; i++)
                                s1 = sum1(x);
                        System.out.println(System.currentTimeMillis() - t);
                        System.out.println(s1);

                        t = System.currentTimeMillis();
                        int s2 = 0;
                        for (int i = 1; i <= 10; i++)
                                s2 = sum2(x);
                        System.out.println(System.currentTimeMillis() - t);
                        System.out.println(s2);
                }
        }

sum1() represents the obvious approach, while sum2() uses an exception to break out of the loop. Note that no loop ending condition is specified, because the Java language guarantees that array indices will be checked, with an exception thrown if an invalid one is found. Using JDK 1.1.3 without Just In Time compilation, the second method runs about 15% faster than the first.

This technique fits into the category of "clever" programming, of the sort that can make code hard to understand, and that relies on certain assumptions about how exceptions are implemented. Given this, it can't be recommended as a general technique. But it may be useful if you're desperate to improve performance, and it illustrates how exceptions are integrated into the Java language.


Native2ascii

In previous issues we've seen how Java programs are composed of ASCII characters, with \uxxxx used to represent Unicode escapes for native characters. Unicode escapes are translated in the first phase of Java compilation.

The JDK contains a small utility program that translates native character sets into ASCII and the reverse. For example, if I have in a file the line:
        \u0061b\u0063

and I say:
        $ native2ascii -reverse infile outfile

then the output file will have:
        abc

In other words, I converted an ASCII representation using Unicode escapes back into its native representation.

This tool doesn't really have much use if you're already using ASCII to formulate your Java programs, but is of value if you're using some other character set.


Informations Method in Class

In issue #013 we talked about reflection, a new feature whereby a running program can examine and manipulate class and primitive types within the program. Reflection relies on hooks in java.lang.Class.

Beyond added support for reflection, Class also has some new methods for querying properties of types. To see how these work, consider an example such as:
        class A {}
        class B extends A {}

        public class test {
                public static void main(String args[])
                {
                        Object obj1 = new int[10];
                        Class c1 = obj1.getClass();
                        System.out.println(c1.isArray());
                        System.out.println(c1.getComponentType());

                        Class c2 = Double.TYPE;
                        System.out.println(c2.isPrimitive());

                        Object obj3 = new A();
                        Object obj4 = new B();
                        System.out.println(obj3.getClass().isInstance(obj4));

                        Class c3 = A.class;
                        Class c4 = B.class;
                        System.out.println(c3.isAssignableFrom(c4));
                        System.out.println(c4.isAssignableFrom(c3));
                }
        }

In the first block of code, we create an array and assign it to an Object reference. We then create a Class object to represent the array, and query the object. isArray() returns true if the Class object is an array, and getComponentType() returns the underlying primitive type ("int" in this case).

isPrimitive() is used to determine whether a Class object type represents a primitive type such as "double".

isInstance() is the programmatic equivalent of the "instanceof" operator. In the example above, we are essentially saying:
        B object instanceof A

which is true, because a subclass object is an instance of its superclass type.

Finally, isAssignableFrom() is used to check whether one class reference can be assigned to another. In this example, a B reference can be assigned to an A one, but not the other way around.

The program output is:
        true
        int
        true
        true
        true
        false

Even if you don't use reflection, these methods are sometimes useful in figuring out what sort of a type is being manipulated. Given a non-null Object reference, you can get its Class object by calling getClass(), and then use these methods.


Properties

In issue #016 we talked about the use of Hashtable to store lists. java.util.Properties is a subclass of Hashtable optimized for storing and manipulating name=value pairs.

A simple example of using the Properties class is this:

        import java.util.Properties;

        public class prop {
                public static void main(String args[])
                {
                        Properties pr = System.getProperties();
                        pr.list(System.out);
                }
        }

which displays system properties. For example, on my system the output is:
        -- listing properties --
        user.language=en
        java.home=e:\java\bin\..
        awt.toolkit=sun.awt.windows.WToolkit
        file.encoding.pkg=sun.io
        java.version=1.1.3
        file.separator=\
        line.separator=

        user.region=US
        file.encoding=8859_1
        java.compiler=symcjit
        java.vendor=Sun Microsystems Inc.
        user.timezone=GMT
        user.name=glenm
        os.arch=x86
        os.name=Windows NT
        java.vendor.url=http://www.sun.com/
        user.dir=G:\tmp
        java.class.path=.;e:/java/lib/classes.zip;e:\java\bin...
        java.class.version=45.3
        os.version=4.0
        path.separator=;
        user.home=g:/.

Besides the features of Hashtable (such as the get() and put() methods), Properties supports the loading/saving of property lists to streams (typically disk files), and supports default properties that are used if a given property cannot be found.

Beyond the use for representing system characteristics, properties are heavily used in internationalization, to provide locale-specific settings. For example, a menu label can be given a tag such as "exitTag", and this name looked up in a property file to find the corresponding value that represents the label text for a given language. PropertyResourceBundle is an example of a class that uses Properties and that is targeted at internationalization support.


Hashcode

hashCode() is a method defined in java.lang.Object, the superclass of all Java class types. It returns a hash code such as is needed by java.Util.Hashtable.

For a given object, hashCode() always returns the same value, when invoked multiple times during one Java program execution. However, the value returned may be different from one execution to another, and therefore permanently storing a hash code value, for example via serialization, does not make sense. It's interesting to note that when a Hashtable is serialized, only the key/value pairs are saved, and not the internally saved hash code values, which may change at the point of deserialization.

Also, hashCode() has the property that if calling Object.equals() to compare two objects returns true, then the hashCode() methods for the two objects must return identical results. Of course, hashCode() can also return identical values for unequal objects.

The Object version of hashCode() will typically calculate the hash code by converting the internal object address into an integer, but there is no guarantee that the code will be computed in this way. Subclasses such as java.lang.String reimplement hashCode() to calculate a value that takes into account the specific characteristics of the particular type of data being manipulated.


IdentityHashcode

Another aspect of hashCode() is a new feature in 1.1, the method System.identityHashCode(). This method can be used to compute the hash code that Object.hashCode() would have come up with, even if the given object's class overrides hashCode().

So, for example, running this program:
        public class test {

                public static void main(String args[])
                {
                        String s = "this is a test";

                        System.out.println(s.hashCode());

                        System.out.println(System.identityHashCode(s));

                        System.out.println(System.identityHashCode(null));
                }

        }

results in three different values being printed. The first one is computed via String.hashCode(). The second one computes the value that Object.hashCode() would come up with, and the third value printed is 0 for the null reference.

This feature is used for doing object serialization, among other places. It is useful when dealing with a variety of object types. Each type may have its own hashCode() suitable for representing that type, but not optimal across other types.


Internalization


Unicode

With this issue, we will be starting a series on internationalization using Java. Internationalization refers to constructing applications such that they can operate using other languages, currencies, dates and times, and so on, with only minimal changes required.

The first aspect of this topic is Unicode, the character set used by Java. Unicode represents characters as 16 unsigned bits, with values in the range 0 - 65535. Every character uses two bytes. Printable ASCII characters have an easy mapping to Unicode, by adding a 0 high byte to the ASCII low byte. So, for example, a space is 0x0020.

Characters can be converted to integers without any cast, while converting the other way requires a cast (and may not always make sense, because an integer is 32 bits). For example, this program:
        public class test1 {
                public static void main(String args[])
                {
                        char c = '\uffff';
                        int i = c;
                        System.out.println(i);
                }
        }

prints 65535. Note that \uNNNN is used to represent arbitrary Unicode characters in hex format.

Unicode can be used to express program identifiers, in addition to its use to express the values of specified characters. For example, this program:
        public class test2 {
                public static void main(String args[])
                {
                        int x\u0430 = 37;
                        System.out.println(x\u0430);
                }
        }

is legal. \u0430 is a letter (Cyrillic small letter A), and therefore can be part of an identifier. Unicode translation takes place early in the Java compilation process. Another aspect of this is that:
        public class test3 {
                public static void main(String args[])
                {
                        char c = '\u000D';
                }
        }

is not a valid program, even though some Java compilers accept it. Section 3.10.4 in the Java Language Specification says that a carriage return (0x000D) may not appear as part of a character literal, and the early Unicode translation means that a literal carriage return does in fact appear, that is, Unicode translation results in:
        char c = '<actual carriage return>';

\r needs to be used as a substitute for \u000D in this example.

java.lang.Character is a class for manipulating Java characters. It has methods for classifying characters, for example as to whether they are digits or letters. There is also a Web site http://www.unicode.org

that presents a lot of detail on how Unicode works. Note also that Java support for Unicode doesn't necessarily mean that a tool using Java (like a Web browser) will have the necessary fonts to display all Unicode characters.

In the next issue we will be looking at some additional aspects of Java character support.


UFT-8 Encoding

In the last issue we saw how Java represents character data with the Unicode character set. Each character requires two bytes or 16 bits.

The ability to support a wide range of different characters is desirable, but potentially wasteful when targeting the many systems that use 8-bit characters, and that have huge volumes of 8-bit textual data stored in databases. To deal with this problem, Java supports the UTF-8 encoding as part of the DataInputStream and DataOutputStream classes. With this encoding, characters are represented as follows:
        \u0000 - \u007F         1 byte          0xxxxxxx

        \u0080 - \u07FF         2 bytes         110xxxxx 10xxxxxx

        \u0800 - \uFFFF         3 bytes         1110xxxx 10xxxxxx 10xxxxxx

So the printable ASCII character set is represented as itself, that is, using only one byte per character (except for the null byte, which is encoded using a two-byte format to avoid embedded nulls in strings). This encoding is thus quite efficient when the bulk of the characters are ASCII.

An example of using UTF-8 looks like this:
        import java.io.*;

        public class utf {
                public static void main(String args[])
                {
                        String tmp = "tmpfile";
                        try {
                                FileOutputStream fos =
                                    new FileOutputStream(tmp);
                                DataOutputStream dos =
                                    new DataOutputStream(fos);
                                dos.writeUTF("testing\n");
                                dos.close();
                                FileInputStream fis =
                                    new FileInputStream(tmp);
                                DataInputStream dis =
                                    new DataInputStream(fis);
                                String instr = dis.readUTF();
                                dis.close();
                                System.out.print(instr);
                        }
                        catch (Throwable e) {
                                System.err.println(e);
                        }
                }
        }

This example writes a string to a file and then reads it back. The string length is written out as a two-byte value before the actual string, so at most 65535 bytes of UTF-8 encoding are supported per string.


Character Encoding

We saw earlier how Java represents characters as 16-bit unsigned values. This supports the representation of a variety of character sets.

But typically Unicode is not used to actually store characters in a disk file. For example, in the United States, 8-bit ASCII is widely used instead, both for text and binary data. We can say that ASCII represents a "local encoding", and there needs to be some way to convert a local encoding into Unicode and back.

One way that this is done is via I/O stream readers and writers. For example, consider this application:
        import java.io.*;

        public class encode {
                public static void main(String args[])
                {
                        String infile = args[0];
                        String outfile = args[1];

                        String encin = "8859_1";  // Latin-1
                        String encout = "8859_5"; // Cyrillic

                        try {
                                InputStream istr =
                                    new FileInputStream(infile);
                                InputStreamReader ird =
                                    new InputStreamReader(istr, encin);
                                BufferedReader br =
                                    new BufferedReader(ird);

                                OutputStream ostr =
                                    new FileOutputStream(outfile);
                                OutputStreamWriter owr =
                                    new OutputStreamWriter(ostr, encout);
                                BufferedWriter bw =
                                    new BufferedWriter(owr);

                                char buf[] = new char[4096];
                                int len;
                                while ((len = br.read(buf)) != -1)
                                        bw.write(buf, 0, len);

                                br.close();
                                bw.flush();
                                bw.close();
                        }
                        catch (Throwable e) {
                                System.err.println(e);
                        }
                }
        }

This program copies its input file to its output file, applying different encodings to each. That is, input bytes are decoded using the 8859_1 encoding (ISO nomenclature for the Latin-1 character set) and output bytes are encoded using the 8859_5 encoding (Cyrillic). So input bytes will be converted to 16 bits via filling the top byte with 0, and output characters (16 bits) will be converted to whatever representation Cyrillic uses.

This approach is more expensive than doing low-level byte I/O, but is important if you're concerned about supporting character sets other than the default one for your locale.


Locales

A "locale" is a means of encapsulating information about a particular country or geographical region or language or culture. In Java the Locale class is used to represent such information. Objects of the class do not provide internationalization behavior in and of themselves, but are used by various other classes as a means of identifying what behavior is desired. Examples might be varying date/time or currency formats.

A simple example of Locale usage is this:

        import java.util.Locale;

        public class testlocale {

                public static void main(String args[])
                {
                        Locale def = Locale.getDefault();
                        System.out.println("Default locale = " + def);

                        Locale ger = Locale.GERMAN;
                        System.out.println(def.getDisplayName(ger));
                }

        }

This program first retrieves the default locale and displays it. Then it displays the default locale, using German as the display language. Output is this:
        Default locale = en_US
        Englisch (Vereinigte Staaten)

There are a set of standard locales defined in Locale. You can also create your own locales, based on strings representing a country, language, and local variant. A variant is specific to a particular implementation and platform.


Local Customs

We saw in a previous issue how locales could be represented using the java.util.Locale class. A locale is a representation of a distinct culture or language or region or set of customs.

To see how locales are used in practice, consider the problem of writing a program that handles calendar dates and currency formats in a locale-independent way. That is, the program should always do the right thing, no matter where it's executed.

An example of handling local customs would be this:
        import java.util.*;
        import java.text.*;

        public class date {

                public static void main(String args[])
                {
                        Date now = new Date();

                        DateFormat df_fr =
                            DateFormat.getDateInstance(DateFormat.LONG,
                            Locale.FRANCE);
                        DateFormat df_us =
                            DateFormat.getDateInstance(DateFormat.LONG,
                            Locale.US);

                        System.out.println(df_fr.format(now));
                        System.out.println(df_us.format(now));

                        NumberFormat pr_fr =
                            NumberFormat.getCurrencyInstance(Locale.FRANCE);
                        NumberFormat pr_us =
                            NumberFormat.getCurrencyInstance(Locale.US);

                        double d = 123.45;
                        System.out.println(pr_fr.format(d));
                        System.out.println(pr_us.format(d));
                }

        }

In this program, we get the current date, and then set up a couple of DateFormat objects, that encapsulate the desired format of the date (SHORT for mm/dd/yy or LONG to have the date spelled out), along with the locale the date is targeted for. We also obtain a NumberFormat object to format currency values.

These objects then have particular values applied to them, for example, the current date, or the value 123.45 in local units of currency (such as francs or dollars). The output of the program is:
        13 avril 1998
        April 13, 1998
        123,45 F
        $123.45

The above example could also be used without specifying particular locales. In such a case, the default locale is used. Note that this approach, using default locales, is not at all the same as assuming particular customs. For example, if I say:
        double d = 123.45;

        System.out.println("$" + d);

then I am assuming particular currency-formatting customs. Making such assumptions may be acceptable, as long as you realize that your application will not work correctly in some other environment that has a different set of customs.


Message Formatting

If you've used the language C very much, you will be familiar with the ubiquitous printf() library function:
        printf("%s %3d %-4ld\n", a, b, c);

for doing formatted output. A related function sprintf() does formatting into a string. C++ has these facilities along with additional ones for doing formatting.

What about formatting in Java? A 1.1 package called java.text is used for this purpose. To see how it works, consider a simple example of formatting error messages in two different styles:
        Error at line 23 of file Test.java: ...

        File Test.java, line 23: ...

An example of this type of formatting is:
        import java.text.*;

        public class format {

                static String fmt1 = "Error at line {1} of file {0}: {2}";
                static String fmt2 = "File {0}, line {1}: {2}";

                public static String format(String fmt, String file,
                    int ln, String msg)
                {
                        Object values[] = new Object[3];
                        values[0] = file;
                        values[1] = new Integer(ln);
                        values[2] = msg;

                        return MessageFormat.format(fmt, values);
                }

                public static void main(String args[])
                {
                        String err1 = format(fmt1, "Test.java", 37, "msg #1");
                        System.out.println(err1);

                        String err2 = format(fmt2, "Test.java", 47, "msg #2");
                        System.out.println(err2);
                }

        }

MessageFormat.format() takes a format string, along with a vector of Objects that contain the values to be formatted. Primitive types like integers are represented via wrappers (Integer in this case). Entries in the format string like "{1}" are replaced with the corresponding value.

The output of this program is:
        Error at line 37 of file Test.java: msg #1

        File Test.java, line 47: msg #2

The format string itself might be read from a property file (see issue #025) or a resource bundle, and thus formatting can be customized on a per-locale basis. In other words, you'd read in the format string from a resource bundle, and then use it along with a set of object values to create an actual string to display in an application.


Sort

Java 1.1 offers no standard way to sort lists of elements. This deficiency has been remedied in 1.2 using a combination of mechanisms. The first of these is the java.lang.Comparable interface, that imposes an ordering on object instances of classes that implement the interface. Such classes include all the standard wrapper classes such as Float and Long, together with the String class. It's therefore possible to say, for example, whether one String is ordered before another for purposes of sorting.

There is an analogous interface java.util.Comparator for specifying user-defined orderings. Comparator.compare(Object, Object) returns a value indicating whether the first object is ordered before the second, is equal to it, or comes after it.

The third new feature is the whole Collections framework. In 1.2 java.util has a number of important new classes and interfaces, that implement collections of elements. These include lists, maps, sets, and trees. The existing Vector class has also been brought into this framework. Collections are a topic of their own that we may discuss at some point.

One of the algorithms implemented for collections is sort(), and thus we can say:
        import java.util.*;

        public class compare {
                public static void main(String args[])
                {
                        Vector vec = new Vector();

                        vec.addElement("apple");
                        vec.addElement("peach");
                        vec.addElement("orange");
                        vec.addElement("banana");
                        vec.addElement("tangerine");

                        Collections.sort(vec);

                        for (int i = 0; i < vec.size(); i++)
                                System.out.println((String)vec.elementAt(i));
                }
        }

This uses the standard java.lang.Comparable ordering defined on Strings. We could impose our own ordering for sort() by specifying a class object that implements java.util.Comparator.


File

We've been looking at some of the classes contained in the Java core libraries. Another interesting one of these is java.io.File. This class is used to represent files, with a variety of operations supported. To illustrate some of the operations, consider a program for walking through a directory structure:
        import java.io.*;
        import java.util.*;

        public class FileWalker {

                private Stack stk = new Stack();

                public FileWalker(String fn)
                {
                        stk.push(fn);
                }

                public synchronized String getNext()
                {
                        if (stk.empty())
                                return null;

                        String fn = (String)stk.pop();
                        File f = new File(fn);

                        if (f.isDirectory()) {
                                String lst[] = f.list();
                                int len = lst.length - 1;
                                for (int i = len; i >= 0; i--) {
                                        File f2 = new File(fn, lst[i]);
                                        stk.push(f2.getPath());
                                }
                        }

                        return f.getPath();
                }

                public static void main(String args[])
                {
                        FileWalker fw = new FileWalker(args[0]);

                        String path = null;
                        while ((path = fw.getNext()) != null)

                                System.out.println(path);
                }

        }

The idea here is to keep a stack of directories/files to be visited, and then visit them in turn. When subdirectories are encountered, their entries are stacked up as well.

File represents a file or directory, and operations such as asking whether a given path is a directory or not are supported. The contents of a directory can be looked up using the list() method, and this feature is used to traverse subdirectories and get their entries. The entries are pushed on the stack and traversed in turn.

Note that we use a File constructor that takes a directory and an entry in that directory, to form a new item to be pushed on the stack for later traversal. And then later we use getPath() to return the actual paths. What we avoid doing is making assumptions about path separators like "\" or "/". This issue is handled internally within the File class.

getNext() is a synchronized method, meaning that the Java Virtual Machine obtains a lock on the underlying object before the method is entered. Why does this matter? If two threads are using a FileWalker object, and both of them call getNext() simultaneously without any synchronization being done, confusion may result due to the fact that getNext() updates its internal state (the stack). Synchronization guarantees that only one thread will execute getNext() at a time.


Instance Initializers

In a previous issue we saw an example of static initializers for classes, where one-time initialization of a class can be performed. Java 1.1 extends this idea to instance initialization, that is, blocks of code in a class that are executed every time a new class instance is created. To get an idea of how this works, consider an example such as:
        class A {

                public A()
                {
                        System.out.println("A ctor");
                }

        }

        class B extends A {

                static {
                        System.out.println("B static init");
                }

                public B()
                {
                        super();
                        System.out.println("B ctor");
                }

                {
                        System.out.println("B instance init");
                }

        }

        public class init {

                public static void main(String args[])
                {
                        B b1 = new B();
                        B b2 = new B();
                }

        }

Here we have a superclass A, and a subclass B. We create two instances of B within main(). The output from running this program is:
        B static init
        A ctor
        B instance init
        B ctor
        A ctor
        B instance init
        B ctor

The static code block in B is executed first, and only one time. Then, for each instance of B that is created, the superclass constructor is run, then the block of code representing an instance initializer in B, and then B's constructor.

Such an instance initializer is similar to a no-argument constructor. So why would you use an initializer like this instead of a constructor? One reason is for initializing instances of anonymous classes (see next section), which do not have names and cannot define their own constructors.

Another use of instance initializers is to support initialization of object fields near the definition of those fields, rather than performing initialization in a constructor which may be some distance away.


Cloneable and Serializable

Here is a very simple one. The core Java libraries contain several interfaces of this type:
        public interface Cloneable {}

alone in a source file. Why would you want to do this, given that the interface doesn't define anything?

This technique can be used to "mark" a class, to specify that it has a given property, and sometimes goes by the name of "marker interface". In the case of Cloneable, Object.clone(), a native method for making a copy of an object, requires that the object implement the Cloneable interface, with an CloneNotSupportedException thrown if not.

So if you'd like a class that you develop to be cloneable, you need to say:
        public class MyClass implements Cloneable { ... }

and this property can then be tested using the "instanceof" operator.

Similar considerations apply for Serializable, that is, a class that supports conversion to/from a byte stream needs to implement Serializable.


I/O Speedups

Suppose that you'd like to count the number of text lines in a file.
One way of doing this is to say:

        import java.io.*;

        public class test4 {

                public static void main(String args[])
                {
                        try {
                                FileInputStream fis =
                                    new FileInputStream(args[0]);
                                DataInputStream dis =
                                    new DataInputStream(fis);
                                int cnt = 0;
                                while (dis.readLine() != null)
                                        cnt++;
                                fis.close();
                                System.out.println(cnt);
                        }
                        catch (Throwable e) {
                                System.err.println("exception");
                        }
                }

        }

This is quite slow, in part because DataInputStream.readLine() does a method call (read()) for each character.

A faster way in Java 1.1 is to say:

        import java.io.*;

        public class test3 {

                public static void main(String args[])
                {
                        try {
                                FileReader fr = new FileReader(args[0]);
                                BufferedReader br = new BufferedReader(fr);
                                int cnt = 0;
                                while (br.readLine() != null)
                                        cnt++;
                                fr.close();
                                System.out.println(cnt);
                        }
                        catch (Throwable e) {
                                System.err.println("exception");
                        }
                }
        }

This runs about 4 times as fast as the old approach, and avoids a read() call for each character. readLine() in this case grabs the underlying data in large buffered chunks.


Class Literals

In Java the class "java.lang.Class" has always been used to represent class and interface types. A statement like:
        Class c = null;

        try {
                c = Class.forName("java.lang.String");
        }
        catch (Throwable e) {
        }

will dynamically load the specified class if it is not already loaded. In Java 1.0 a Class object can also represent arrays, as a special type of class.

In Java 1.1, this notion has been broadened to include any Java type, and there is an easier way to obtain the Class object for a type. For example, the following code prints "equal":
        public class test1 {

                public static void main(String args[])
                {
                        try {
                                Class c1 = Class.forName("java.lang.String");
                                Class c2 = java.lang.String.class;
                                if (c1 == c2)
                                        System.out.println("equal");
                        }
                        catch (Throwable e) {
                        }
                }

        }

In this example the new approach uses ".class" appended to a class specification to get the Class object reference.

This feature can also be used on fundamental types, for example:
        public class test2 {

                public static void main(String args[])
                {
                        Class c1 = Integer.TYPE;
                        Class c2 = int.class;

                        if (c1 == c2)
                                System.out.println("equal");
                }

        }

That is, a type like "int" can be represented via a Class object.

One reason why this new feature is important is that Java 1.1 also introduces nested or inner classes, classes defined within other classes. Java uses a name like "A$B.class" to name a disk file that refers to a class B nested in a class A, and using the older Class.forName() approach requires knowledge of this encoding.


BitSet

A simple but valuable class in java.util is BitSet, used for keeping track of a set of boolean values. For example, this code:
        import java.util.*;

        public class test5 {

                public static void main(String args[])
                {
                        BitSet b = new BitSet();

                        b.set(57);
                        b.set(109);
                        b.set(0);
                        b.clear(109);

                        for (int i = 0; i < 200; i++) {
                                if (b.get(i))
                                        System.out.print(i + " ");
                        }

                        System.out.println();
                }

        }

displays the output:
        0 57

The set of values grows dynamically as needed. It is also possible to compare two sets, or perform AND, OR, and XOR operations.


Anonynous Array

We are going to spend a few columns looking at some of the new 1.1 language features. The biggest of these is inner classes, which warrants its own set of columns.

One new feature is anonymous arrays. In Java 1.0, you could say things like:
        int x[] = {1, 2, 3};

while saying:
        int x[];
        x = {1, 2, 3};

was illegal, because the {} was supported only in initializer expressions following a declaration.

This usage is not legal in 1.1 either, but instead you can say:
        int x[];
        x = new int[] {1, 2, 3};

That is, instead of using "new int[3]", leave out the size and follow the [] with a list of the actual initializer values.


Stack

In Java the Vector class (java.util.Vector) is widely used in a variety of ways, to store lists of objects.

A lesser-known class, that is a subclass of Vector, is Stack. Stack supports a last-in-first-out (LIFO) set of objects, and uses the usual operations -- push(), pop(), and so on. For example, this program will display a list of numbers in descending order:
        import java.util.*;

        public class stack {

                public static void main(String args[])
                {
                        Stack st = new Stack();

                        for (int i = 1; i <= 10; i++)
                                st.push(new Integer(i));

                        while (!st.empty())
                                System.out.println(st.pop());
                }

        }

There is also a peek() method for examining the top of stack value without popping it.

Stack is derived from Vector, and as such, the methods of Vector are also available to a Stack user. But it is perhaps not a good idea to take advantage of this fact, for example by inserting a value into the middle of the stack. Doing so breaks the stack paradigm and makes it harder to reason about the operation of a program.


Accessing Instance Variables

Consider a bit of code like:
        public class perf1 {

                private char buf[] = new char[4096];

                public int find(char c)
                {
                        for (int i = 0; i < buf.length; i++) {
                                if (buf[i] == c)
                                        return i;
                        }

                        return -1;
                }

                public static void main(String args[])
                {
                        perf1 p = new perf1();

                        int n = 2500;
                        while (n-- > 0)
                                p.find('x');
                }

        }

where there is an object instance of a class, with an internal buffer that you want to search repeatedly. This code runs pretty slowly (around 14.5 seconds on a slow Pentium with JDK 1.1.2). Is there any way to speed it up?

There are a couple of improvements that can be made, centering around accessing instance variables (fields) of an object. It turns out that such access is quite slow, with calls to the "getfield" instruction in the Java Virtual Machine. That is, the above code accesses "this.buf" twice in each iteration of the loop in find(), once for determining buf.length and the other for accessing buf[i].

Rewriting the code as:
        public class perf2 {

                private char buf[] = new char[4096];

                public int find(char c)
                {
                        char xbuf[] = buf;

                        for (int i = 0; i < xbuf.length; i++) {
                                if (xbuf[i] == c)
                                        return i;
                        }

                        return -1;
                }

                public static void main(String args[])
                {
                        perf2 p = new perf2();

                        int n = 2500;
                        while (n-- > 0)
                                p.find('x');
                }

        }

results in around a 15% speedup, with the reference to "this.buf" replaced by a local stack-based variable "xbuf". This simply adds another reference to buf, with no copying of data or anything implied. A Java compiler might be able to automatically supply this optimization.

A further speedup can be realized by saying:

        public class perf3 {

                private char buf[] = new char[4096];

                public int find(char c)
                {
                        char xbuf[] = buf;

                        int n = xbuf.length;

                        for (int i = 0; i < n; i++) {
                                if (xbuf[i] == c)
                                        return i;
                        }

                        return -1;
                }

                public static void main(String args[])
                {
                        perf3 p = new perf3();

                        int n = 2500;
                        while (n-- > 0)
                                p.find('x');
                }

        }

to eliminate calls to the JVM "arraylength" instruction. This gives another 10%, or a total of 25% from the original code.

Of course, such tweaking has a downside, leading to more obscure code. But it's a useful performance improvement in certain cases where instance variables in a class are heavily accessed.


Wrapper Classes

In Java, it is possible to represent a set of primitive types like integers in at least two ways. One is simply as an array of actual values:
        int x[] = {1, 2, 3};

while the other uses a wrapper class:
        Integer x[] = {new Integer(1), new Integer(2), new Integer(3)};

In terms of actually representing a set of values, the first of these is more efficient in speed and space, while the second is more general. As a rule of thumb, representing an integer via a wrapper takes about 12-16 bytes, compared to 4 in an actual array of ints. And retrieving the value of the integer using Integer.intValue() may involve a method call.

But if you need to store a list of integers in a Vector or Hashtable, or you'd like to store an integer in a mixed list of values (for example, Integers and Strings), then the wrapper is the only way to go. Wrapper classes exist for all primitive types, with some of the wrapper classes such as Short added in 1.1.

These classes have another important function, which is to group together a set of methods that operate on actual primitive types. For example, consider the class (static) method Integer.toHexString(int). This can be used to display an integer in hex format:
        public class test {
                public static void main(String args[])
                {
                        int i = 123456;
                        System.out.println(Integer.toHexString(i));
                }
        }

Beyond the usual methods for converting primitive types to Strings and back again, and the accessor methods like intValue(), the wrapper classes also define constants for a given primitive type. For example, Short.MAX_VALUE is 32767, the largest value that may be represented by a short. Float and Double also have constants for negative and positive infinities and for NaN (a special constant, not-a-number).

The Character class has a variety of methods for categorizing characters, for example the isLetter(char) method. These methods work by consulting a large table found within Character.java in the JDK source code. If you are used to using C/C++, these methods may be familiar, but their implementation is far more complex because the Unicode character set is used. What constitutes a letter in Unicode is quite different than asking a similar question when using the ASCII character set.


Sizeof for Java

In previous issues we've mentioned that Java has no sizeof() operator like C/C++. With uniform sizes for primitive data types, and a different style of memory allocation, the need for sizeof() really isn't there. And it's hard to define what sizeof() would mean anyway, given that an object may not contain other objects, but only references to them.

But it's interesting to experiment with the 1.1 reflection feature and see whether a method can be devised that will return useful information about object sizes.

The Sizeof class below tries to do this, for a passed-in data structure. It walks the structure and tallies up the total size in bytes. It ignores alignment and packing issues and hidden fields in structures, and assumes a boolean is of size 1 and a reference of size 4 (reference sizes may vary; for example SZ_REF might be 8 on a machine with 64-bit pointers).

It does not count static data members of class instances, but does include members inherited/implemented from superclasses and interfaces. It does not follow references in object instances or in arrays, except for the case of a multi-dimensional array, where the reference is to another array.

Included are some tests that illustrate what results are expected in given cases.
        import java.lang.reflect.*;

        class test_Class1 {
                private int a1;
                public byte a2;
                protected char a3[];
                static byte a33;
        }

        class test_Class2 extends test_Class1 {
                static float a33;
                byte a3;
                short a4;
                double a5[] = new double[10];
        }

        class test_Class3 extends test_Class2 {
                boolean a4;
                static int a44;
                char a5;
                long a6;
        }

        interface test_Class4 {
                public byte x = 1;
        }

        interface test_Class5 extends test_Class4 {
                public int x = 2;
        }

        class test_Class6 {
                public double x;
        }

        class test_Class7 extends test_Class6 implements test_Class5 {
                char x[] = null;
        }

        public class Sizeof {

                private static final int SZ_REF = 4;

                public static int sizeof(boolean b)      {  return 1;  }
                public static int sizeof(byte b)         {  return 1;  }
                public static int sizeof(char c)         {  return 2;  }
                public static int sizeof(short s)        {  return 2;  }
                public static int sizeof(int i)          {  return 4;  }
                public static int sizeof(long l)         {  return 8;  }
                public static int sizeof(float f)        {  return 4;  }
                public static int sizeof(double d)       {  return 8;  }

                private static int size_inst(Class c)
                {
                        Field flds[] = c.getDeclaredFields();
                        int sz = 0;

                        for (int i = 0; i < flds.length; i++) {
                                Field f = flds[i];
                                if (!c.isInterface() &&
                                    (f.getModifiers() & Modifier.STATIC) != 0)
                                        continue;
                                sz += size_prim(f.getType());
                        }

                        if (c.getSuperclass() != null)
                                sz += size_inst(c.getSuperclass());

                        Class cv[] = c.getInterfaces();
                        for (int i = 0; i < cv.length; i++)
                                sz += size_inst(cv[i]);

                        return sz;
                }

                private static int size_prim(Class t)
                {
                        if (t == Boolean.TYPE)          return 1;
                        else if (t == Byte.TYPE)        return 1;
                        else if (t == Character.TYPE)   return 2;
                        else if (t == Short.TYPE)       return 2;
                        else if (t == Integer.TYPE)     return 4;
                        else if (t == Long.TYPE)        return 8;
                        else if (t == Float.TYPE)       return 4;
                        else if (t == Double.TYPE)      return 8;
                        else if (t == Void.TYPE)        return 0;
                        else                            return SZ_REF;
                }

                private static int size_arr(Object obj, Class c)
                {
                        Class ct = c.getComponentType();
                        int len = Array.getLength(obj);

                        if (ct.isPrimitive()) {
                                return len * size_prim(ct);
                        }
                        else {
                                int sz = 0;
                                for (int i = 0; i < len; i++) {
                                        sz += SZ_REF;
                                        Object obj2 = Array.get(obj, i);
                                        if (obj2 == null)
                                                continue;
                                        Class c2 = obj2.getClass();
                                        if (!c2.isArray())
                                                continue;
                                        sz += size_arr(obj2, c2);
                                }
                                return sz;
                        }
                }

                public static int sizeof(Object obj)
                {
                        if (obj == null)
                                return 0;

                        Class c = obj.getClass();

                        if (c.isArray())
                                return size_arr(obj, c);
                        else
                                return size_inst(c);
                }

                private static void err(String s)
                {
                        System.err.println("*** " + s + " ***");
                }

                private static void test()
                {
                        if (sizeof(null) != 0)
                                err("null");

                        if (sizeof(true) != 1)         err("boolean");
                        if (sizeof((byte)37) != 1)     err("byte");
                        if (sizeof('x') != 2)          err("char");
                        if (sizeof((short)37) != 2)    err("short");
                        if (sizeof(37) != 4)           err("int");
                        if (sizeof(37L) != 8)          err("long");
                        if (sizeof(37.0f) != 4)        err("float");
                        if (sizeof(37.0) != 8)         err("double");

                        if (sizeof(new boolean[0]) != 0)
                                err("boolean[0]");
                        if (sizeof(new byte[10]) != 10)
                                err("byte[10]");
                        if (sizeof(new char[10][10]) != 200 + 10 * SZ_REF)
                                err("char[10][10]");
                        if (sizeof(new short[10][11][12]) != 2640 +
                            120 * SZ_REF)
                                err("short[10][11][12]");
                        if (sizeof(new int[0][10]) != 0)
                                err("int[0][10]");

                        if (sizeof(new String[100]) != 100 * SZ_REF)
                                err("String[100]");
                        if (sizeof(new String[10][10]) != 110 * SZ_REF)
                                err("String[10][10]");

                        Object ov[] = new Object[3];
                        ov[0] = new byte[10];
                        ov[2] = new double[10];
                        if (sizeof(ov) != 90 + 3 * SZ_REF)
                                err("Object[3]");

                        String sv[] = new String[10];
                        for (int i = 0; i < 10; i++)
                                sv[i] = new String();
                        if (sizeof(sv) != 10 * SZ_REF)
                                err("String[10]");

                        if (sizeof(new Object()) != 0)
                                err("Object");
                        if (sizeof(new Integer(37)) != 4)
                                err("Integer(37)");

                        if (sizeof(new test_Class1()) != 5 + SZ_REF)
                                err("test_Class1");
                        if (sizeof(new test_Class2()) != 8 + 2 * SZ_REF)
                                err("test_Class2");
                        if (sizeof(new test_Class3()) != 19 + 2 * SZ_REF)
                                err("test_Class3");

                        if (sizeof(new test_Class7()) != 13 + SZ_REF)
                                err("test_Class7");
                }

                public static void main(String args[])
                {
                        test();
                }
        }


Printing

People often ask about how to do printing in Java. There are at least two ways this can be done. We will illustrate one approach here, and another later in this issue.

One simple way to print is to use device files. With UNIX, this involves writing into the file /dev/lp, and with Windows, use of a file called "lpt1". Bytes written into such a file show up on the printer. For example:
        import java.io.*;

        public class lpr {

                public static void main(String args[])
                {
                        try {
                                FileOutputStream fos =
                                    new FileOutputStream("lpt1");
                                String s = args[0];
                                for (int i = 0; i < s.length(); i++)
                                        fos.write((byte)s.charAt(i));
                                fos.write((byte)0xC);
                                fos.close();
                        }
                        catch (Throwable e) {
                                System.err.println("*** exception ***");
                        }
                }
        }

This will print the specified string on the printer, and then issue a form feed (0xC).

Normally, "lpr" would be expanded to do several additional things, for example expanding tabs, putting margins at the top and bottom and sides of the page, and handling multiple copies.

This particular example sidesteps the issues of coordinating printing with a print queue manager, displaying Unicode, remote printing, and so on.

Below is another printing example, this one using the Abstract Windowing Toolkit.


StringBuffer

In Java String objects are immutable, that is, they cannot be changed once created. For example:
        String s = "abc";
        String t = s;

        s = s.toUpperCase();

        System.out.println(t);

displays "abc", not "ABC". Both s and t start out referring to a String object. That object does not change when the upper case conversion is done, but instead, another object is created and s is made to refer to it.

For operations involving mutable strings, the class StringBuffer can be used. Some of its methods, like charAt(), overlap those found in String, but many of the operations focus on building up new strings of characters. There are many append() methods, for example.

For example, the "+" operator on strings in Java is implemented by replacing a line such as:
        String s = "xyz" + 37;

with:
        s = (new StringBuffer("xyz")).append(37).toString();

For accumulating strings, a quick and dirty approach like:
        String s = "";

        s += "xyz";

can be used, but this is a lot more expensive than using StringBuffers, because the String must be converted to a StringBuffer and back again. In issue #004 we gave an example where using a StringBuffer resulted in about a 6X speedup. In this example, we would say:
        StringBuffer sb = new StringBuffer();

        sb.append("xyz");

        String s = sb.toString();

In the next section we will look further at StringBuffer performance.


Appending Chars

One of the most common uses of StringBuffer is to accumulate a set of characters, and then convert the result to a String:
        StringBuffer sb = new StringBuffer();

        sb.append('a');
        sb.append('b');
        sb.append('c');

        String s = sb.toString();

Is it possible to improve on the performance of the StringBuffer append() method? At the moment, the answer appears to be yes:
        public class AppendChar {
                private char buf[] = null;
                private int len = 0;

                public AppendChar()
                {
                        buf = new char[16];
                }

                public synchronized void append(char c)
                {
                        if (len == buf.length) {
                                char x[] = new char[len * 2];
                                System.arraycopy(buf, 0, x, 0, len);
                                buf = x;
                        }
                        buf[len++] = c;
                }

                public String toString()
                {
                        return new String(buf, 0, len);
                }
        }

This runs about twice as fast as the standard version, because it internally handles checking of overflow, instead of calling ensureCapacity().

Whether it is "right" to roll your own version of a library method depends on how desperate for speed you are. Most of the time, append() speed doesn't matter, but when it does, this approach may be useful. One of the classic performance bottlenecks is that of doing lots of operations for every character of input in a program.

If you're particularly interested in Java performance, see the Web page http://www.glenmccl.com/~glenm/perflib/index.html

for further information about this type of approach. This page describes a Java performance library.


String Tokenizer / HashTable

Java is similar to other programming languages in that it consists of the actual Java language, with libraries added on. We're going to use the generic term "library" to refer to what is often called an "API" or a "package".

The term "core" is also used to refer to certain of the libraries that are required to run a Java program. Core libraries include:
        java.lang

        java.util

        java.io

The language proper "knows" about some of these. For example, the operator "+" has a certain meaning when applied to Strings. And standard I/O that is made available to a running Java program relies of course on the java.io package.

We will be exploring the use of some of the library classes in a series of columns. In the first one, we'll look at two classes found in java.util, Hashtable and StringTokenizer, and a helper interface known as Enumeration.

Suppose that you'd like to write a program that reads from a file and displays a list of all the unique words in the file. How might this be done? We'll assume that a "word" is any sequence of characters delimited by whitespace.

One approach looks like this:
        import java.util.*;
        import java.io.*;

        public class Word {

                public static void doit(String fn)
                {
                        Hashtable ht = new Hashtable();

                        try {
                                FileInputStream fis = new FileInputStream(fn);
                                DataInput d = new DataInputStream(fis);
                                String s = null;
                                while ((s = d.readLine()) != null) {
                                        StringTokenizer st =
                                           new StringTokenizer(s);
                                        while (st.hasMoreTokens())
                                                ht.put(st.nextToken(), "");
                                }
                                fis.close();
                        }
                        catch (Throwable e) {
                                System.err.println("*** exception ***");
                        }

                        Enumeration en = ht.keys();
                        while (en.hasMoreElements())
                                System.out.println((String)en.nextElement());
                }

                public static void main(String args[])
                {
                        doit(args[0]);
                }

        }

We first set up to read lines from a disk file. As each line is read, we create a StringTokenizer class instance. This class is used to split a String into pieces. By default, the delimiters are space, tab, carriage return, and newline (" \t\r\n"), but you can specify your own set of delimiters to the StringTokenizer constructor.

We iterate based on hasMoreTokens(), picking off each token in turn. These tokens, as Strings, are added to the hash table. put() takes two Object parameters, a key and a value. A String is derived from Object, and may be supplied as a key argument. We don't care about the associated value with the String key, and so we use "" for this. If we did care, for example to count how many times each word occurs, we could set up a Counter class type and supply an instance of it as the value.

A hash table contains only one instance of each key. To find out what words were encountered, we can retrieve the keys and display them. This is done via an Enumeration. Enumeration is not a class, but an interface. That is, it specifies a set of methods that a class that "implements" it must define. The two methods in this case are hasMoreElements() and nextElement(). The keys() method in Hashtable returns an HashTableEnumerator class instance. Because it implements Enumeration, it can be manipulated in a way similar to the superclass / subclass paradigm, through a reference to an Enumeration.

The key hook required to make a Hashtable work is the hashCode() method defined in Object and overridden by derived classes such as String. hashCode() supplies the raw value used to insert an entry into the table.

The approach outlined here is quite simple to use. If you are desperate for performance, some lower-level techniques are faster, though not as convenient.


Javaw

Suppose that you'd like to take the program from the previous section and run it in the Windows environment, using an icon to invoke it (rather than from the command line). How would you do this? There is a program called "javaw" that comes with the JDK. You can use it to run standalone AWT programs using a Windows interface.

To do this, you need to set up a new Program Item in the standard way, with entries like:
        Description:  Delegate

        Command Line:  e:\java\bin\javaw Delegate (this will vary)

        Working Directory:  g:\tmp (this will vary)

When you click on the icon, the program will come up.

There's a certain amount of magic in making something like this work, but if you're interested in this area, you might try experimenting with this technique.


Serialization

Imagine that you have a complex data structure in memory, one with many internal links and fields in it. At the end of the execution of an application, you'd like to somehow save this data structure permanently and then restore it for the next execution of the application.

Java serialization, new in version 1.1, is a way of doing this. It's a mechanism for turning a data structure into a stream of bytes, which can be written to a file, and then read back in to another data structure.

Let's look at a simple example of this:
        // file write.java

        import java.io.*;

        public class write {
                private static final int N = 25;

                public static void main(String args[])
                {
                        int x[][] = new int[N][2];

                        for (int i = 0; i < N; i++) {
                                x[i][0] = i;
                                x[i][1] = i * i;
                        }

                        try {
                                FileOutputStream fos =
                                    new FileOutputStream("xxx");
                                ObjectOutputStream oos =
                                    new ObjectOutputStream(fos);
                                oos.writeObject(x);
                                oos.flush();
                                fos.close();
                        }
                        catch (Throwable e) {
                                System.err.println("exception thrown");
                        }
                }
        }

        // file read.java

        import java.io.*;

        public class read {
                public static void main(String args[])
                {
                        int x[][] = null;

                        try {
                                FileInputStream fis =
                                    new FileInputStream("xxx");
                                ObjectInputStream ois =
                                    new ObjectInputStream(fis);
                                x = (int[][])ois.readObject();
                                fis.close();
                        }
                        catch (Throwable e) {
                                System.err.println("exception thrown");
                        }

                        for (int i = 0; i < x.length; i++)
                                System.out.println(x[i][0] + " " + x[i][1]);
                }
        }

In this example, we build a table of squares in a two-dimensional array, then write the array to a file using writeObject(). We then read the array back into a separate program using readObject(), casting the Object reference to the appropriate type.

This operation may not seem like much, but it's hard to do in other languages, and it's necessary to devise various ad hoc methods for doing so.

For a class to be serializable, it must implement the Serializable interface:
        public class xxx implements java.io.Serializable {
                // stuff
        }

This interface is empty, and simply serves as a flag to allow you to specify which classes are serializable. In the example above, the object we serialized was an array, treated as a class type by Java.

Classes which require special handling during serialization can implement their own writeObject() and readObject() methods.

There are several interesting quirks with serialization which we may discuss at some point.


Reflection

One of the interesting 1.1 features is something known as reflection, where it is possible to query a class at run time to determine its properties. For example, with this code:
        import java.lang.reflect.*;

        public class Dump {
                public static void main(String args[])
                {
                        try {
                                String s = "java.lang." + args[0];
                                Class c = Class.forName(s);
                                Method m[] = c.getMethods();
                                for (int i = 0; i < m.length; i++)
                                        System.out.println(m[i].toString());
                        }
                        catch (Throwable e) {
                        }
                }
        }

one can query a class for the names and properties of its public methods, using the new package "java.lang.reflect". There is also support for accessing private and package level methods. Additionally, you can dynamically invoke methods on a given object.

Running this program by saying:
        $ java Dump Object

results in:
        public final native java.lang.Class java.lang.Object.getClass()
        public native int java.lang.Object.hashCode()
        public boolean java.lang.Object.equals(java.lang.Object)
        public java.lang.String java.lang.Object.toString()
        public final native void java.lang.Object.notify()
        public final native void java.lang.Object.notifyAll()
        public final native void java.lang.Object.wait(long)
        public final void java.lang.Object.wait(long,int)
        public final void java.lang.Object.wait()

This type of feature simply doesn't exist in a language like C or C++. Certain programming environments or debuggers may offer an equivalent, but not as part of the language or its core libraries.


Conditional Compilation

In issue #011 we talked about Java's lack of a preprocessor, such as used in C and C++. This lack is both a blessing and a curse. One common idiom in C is to use conditional compilation, for example, the use of DEBUG to control program execution and possibly take different action such as dumping out more information to the user:
        #ifdef DEBUG

        debugging stuff ...

        #endif

The preprocessor runs before the compiler proper and so the use of #ifdef results in different subsets of the program actually being compiled based on which constants (such as DEBUG) are set.

In Java, we could achieve a similar end by saying something like:
        public class test1 {

                public static final boolean DEBUG = false;

                public static void main(String args[])
                {
                        for (int i = 1; i <= 10; i++) {
                                if (DEBUG)
                                        System.out.println("i = " + i);
                        }
                }
        }

which will dump out the loop index at each loop iteration, if DEBUG is true.

There is a quirk with this worth noting. A program like:
        public class test2 {

                void f()
                {
                        int x = 0;

                        while (false)
                                x = -37;
                }
        }

is invalid, even thought it's superficially like the one above, in that in one case we have:
        if (false)
                statement;

and in the other:
        while (false)
                statement;

In both cases the statement is unreachable, but Java special cases the "if" case, in order to support conditional compilation. See 14.19 in the "Java Language Specification" for a more detailed discussion of this point.

This approach to "conditional compilation" still requires that the code be valid. That is, with use of the C/C++ preprocessor conditional compilation is textually based, in that excluded code is never presented to the actual compiler, whereas the scheme above is logically based -- the code is simply never executed.


JACK

Lex and Yacc are widely-used tools for doing lexical scanning and parsing of input. You describe the structure of the input and these tools generate programs (in C) that will accept and structure such input. For example, an identifier might be lexically described as:
        [a-zA-Z_][a-zA-Z_0-9]*

meaning that the identifier starts with a letter or underscore, followed by zero or more letters, underscores, or digits.

Similarly, a simple expression in BNF (Backus Naur Form) could look like:
        E -> T | E + T

        T -> F | T * F

        F -> number | ( E )

This is known as a grammar, and programming languages like Java are formally described via a grammar. O'Reilly and others publish books on Lex/Yacc, if you want information on these tools.

Jack is a new tool that combines Lex and Yacc capabilities. It's written in Java and produces Java output, that is, produces Java programs for parsing input described via a grammar. Jack is available on the Web at http://www.suntest.com/Jack

You need the Java Development Kit 1.0.2 to use this program. With Jack you describe the lexical and syntactic structure of your program, and then have it produce Java programs that understand this structure.

As an example of a problem made simple by a tool of this type, consider the issue of extracting from Java programs basic information about what is defined where, for example, where a specific class or method is found in the source code. With Jack, it's simply a matter of dumping out key information whenever a particular construct like a method declaration is found.

For example, we might have this format for the output:
        String.charAt method 204 java/src/java/lang/String.java

meaning that the charAt() method of class String is defined at line 204 of String.java.

An index produced in this format, representing all the classes, interfaces, methods, and constructors for JDK 1.0.2, is available via FTP at ftp://ftp.glenmccl.com/morespace1/glenmccl/jack

along with a simple UNIX shell script browser to search and access any of the symbols in the database.

There are many other possibilities for the use of a tool of this type, for example test coverage or generation of class metrics. If you have interest in this area, please send me a note (glenm@glenmccl.com).


JavaScript

You may have heard the term "JavaScript". What is its relation to Java and HTML? JavaScript started out as LiveScript and its connection to Java is fairly loose, sharing some Internet heritage but not that much else.

HTML (Hyper Text Markup Language) is not really a programming language in the usual sense of the term, but rather a language that describes what a page ought to look like. You can't really "do anything" with HTML.

Java is a general purpose programming language. One type of Java program, an applet, is downloadable by a Web browser and then executed locally by the browser. This is useful, for example, in communicating with remote databases. An applet's invocation is described via the HTML tags <applet> and </applet>.

JavaScript can be viewed as a language something like Perl, or as an extension of HTML to allow customized functionality. As an example of what a JavaScript application would look like, consider this example:
        <html>
        <body>

        <a href="link1.html">Link #1</a>
        <br>
        <a href="link2.html">Link #2</a>
        <br>

        <script>

        <!--

        function doit()
        {
                document.write("<br>This page's links are:<br><br>");
                for (var i = 0; i < document.links.length; i++) {
                        var s = "";
                        s += (i + 1) + ".  ";
                        s += document.links[i];
                        s += "<br>";
                        document.write(s);
                }
        }

        doit()

        <!-- -->

        </script>

        </body>
        <html>

This particular application iterates over the links in a page and displays them in a numbered list:
        1.  link1.html

        2.  link2.html

JavaScript as a programming language has a flavor something like Perl or Awk, with weak typing and domain-specific system variables like "document.links[]".

Note that the output of a JavaScript script is HTML, so for example we use "<br>" instead of "\n" to go to the next line. In the above example, the function doit() is defined, and then called to produce HTML.

We will probably not say more about JavaScript, but there are a variety of books available on the topic. You need a Web browser like Netscape 3.0 to execute JavaScript programs.


Binary Search Class

As we've discussed previously, there are still some issues around the performance of Java, and thus there is a premium on the use of efficient algorithms.

One such algorithm is the familiar one of binary search, where a sorted list is searched by continually halving the search area until the desired element is found or it is determined that the element is not in the list.

In Java this might look like:
        import java.util.Vector;

        public class Search {

                // no one should instantiate this class
                // since it exists only as a packaging vehicle
                // for the static method search()

                private Search() {}

                public synchronized static int search(Vector v, Object objp,
                    Orderable op)
                {
                        if (v == null || objp == null || op == null)
                                throw new IllegalArgumentException("null arg");

                        int low = 0;
                        int high = v.size() - 1;
                        while (low <= high) {
                                int mid = (low + high) / 2;
                                int c = op.compareTo(objp, v.elementAt(mid));
                                if (c < 0)
                                        high = mid - 1;
                                else if (c > 0)
                                        low = mid + 1;
                                else
                                        return mid;
                        }
                        return -1;
                }
        }

where an index 0 <= index < N is returned, or -1 if the element is not found. The Vector class found in Java does not automatically maintain a sorted order for the items in the vector.

This algorithm is straightforward save for the notion of an
Orderable. What is this? An Orderable is an interface:

        public interface Orderable {
                public int compareTo(Object p1, Object p2);
        }

If a given class implements an interface, that means that it defines the methods of the interface. This is the method of choice at the moment for passing a method reference as an argument, or in C/C++ terms, passing a pointer to a function. In other words, we create an instance of a class that implements the Orderable interface, which means that the instance will have a compareTo() method that we can call out to.

To see how this works, consider this example that uses the search class:
        import java.util.*;

        class Ord implements Orderable {
                public int compareTo(Object p1, Object p2)
                {
                        int n1 = ((Integer)p1).intValue();
                        int n2 = ((Integer)p2).intValue();

                        if (n1 > n2)
                                return 1;
                        else if (n1 < n2)
                                return -1;
                        else
                                return 0;
                }
        }

        public class test {
                public static void main(String args[])
                {
                        Vector v = new Vector();
                        int N = 10000;
                        int i = 0;

                        for (i = 0; i < N; i++)
                                v.addElement(new Integer(i));

                        for (i = 0; i < N; i++)
                                Search.search(v, new Integer(i), new Ord());
                }
        }

We create a Vector of 10000 integers, each wrapped in an Integer wrapper. We then search for each in turn, passing to Search.search() an object instance of Ord, a class that implements the Orderable interface and thus is guaranteed to contain a compareTo() method for comparing two Object references that refer to Integer wrappers.

This approach is somewhat like the bsearch() library function in ANSI C. A similar technique can be used for sorting. The above search class is part of a Java class library described on the Web page:
        http://www.glenmccl.com/~glenm/javalib/index.html



The Finally clause and Method Exiting

Java has no "goto" statement, though this identifier is reserved in the language. There are several ways in which goto is used in C and C++, and it's interesting to consider the Java alternatives to such usage. In this section we will discuss one alternative, and in the next section another.

One way that goto is used is to jump to the end of a function body, where cleanup can be done. For example, suppose that we are manipulating calendar dates, and have a function where we want a year in the range 1900-99 and evenly divisible by 4. In C, we might have:
        void f(int d)
        {
                if (d < 1900)
                        goto err;
                if (d > 1999)
                        goto err;
                if (d % 4)
                        goto err;

                /* do stuff with date ... */

                return;

        err:
                fprintf(stderr, "invalid date %d\n", d);
        }

In Java, we can achieve a similar end without goto, by using a form of the try-catch-finally statement used in exception handling:
        public class test {

                public static void f(int d)
                {
                        boolean err = true;

                        try {
                                if (d < 1900)
                                        return;
                                if (d > 1999)
                                        return;
                                if (d % 4 != 0)
                                        return;
                                err = false;
                                // do stuff with date ...
                        }
                        finally {
                                if (err)
                                        System.err.println("invalid date "
                                            + d);
                        }
                }

                public static void main(String args[])
                {
                        f(1852);
                        f(1976);
                        f(1989);
                }
        }

The code within the try block is "tried", that is, executed. After the execution of this code, the finally block is executed -- no matter what happens in the try block. In the example above, we exit the method (return) for various error conditions, but when the method is exited, the finally block is executed. In this way, we can execute a series of Java statements, and guarantee that no matter what happens in those statements, some other processing will follow.

Whether this programming style is "good" is a matter of opinion. Saying "return" with the idea that some cleanup will be done by a finally block could be viewed as a little sneaky or confusing, or alternatively this might turn out to be a common Java idiom in a few months or years. At the least, you might put in a comment like:
        if (condition)
                return;         // proceed to cleanup phase of method

If an exception is thrown in a try block, and there is a local catch block to handle it, the catch block is executed, and then the finally block. If there is not a local catch block, the finally block is executed, and then the exception is propagated to the nearest catch block that can handle the exception (that is, the stack is unwound, as in other exception processing).

We will say more about Java exception handling at some future point.


Garbage Collection and Setting to Null

In issue #001 we talked about Java garbage collection, where the runtime system automatically reclaims dynamic storage that is no longer in use. For example:
        public class test {
                public void f()
                {
                        char buf[] = new char[1024];
                        // stuff that uses buf
                }
        }

When method f() exits, the space that buf uses is garbage, because only a local variable in an invalid stack frame references it. The runtime system may eventually reclaim the storage.

Garbage collection is automatic. But sometimes there are ways to help it out. Consider a case where you are managing a stack of Object references:
        public class Stack {

                private static final int MAXLEN = 10;

                private Object stk[] = new Object[MAXLEN];

                private int stkp = -1;

                // add in logic for error checking, stack growing, etc. ...

                public void push(Object p)
                {
                        stk[++stkp] = p;
                }

                public Object pop()
                {
                        return stk[stkp--];
                }
        }

Consider a case where the stack has two elements on it, and you pop one of them. At this point stk[0] will have a valid element in it, and stk[1] will have the element just popped. That is, stk[1] will have a reference to an Object, which could be a reference to anything, including a large data structure of many thousands of bytes. In such a case, this data structure cannot be garbage collected, even though it may no longer be in use.

To remedy this problem, we can rewrite pop() like so:
        public Object pop()
        {
                Object p = stk[stkp];
                stk[stkp--] = null;
                return p;
        }

We haven't nullified the Object itself, just a no longer valid reference to it. The Stack object itself may have a long lifetime, and rewriting the pop() method in this way helps ensure that garbage collection can be done in a timely way.


Fast I/O for Text Lines

In issue #002 we traced through the steps involved in doing Java character output. The Java I/O system offers flexibility in structuring various sorts of I/O, with the possibility of adding filtering layers and so on.

But this flexibility has a performance cost, caused in part by the layers themselves and in part by the method call overhead. As we've said previously, Java is a young language and it's not totally clear how various pieces will shake out. But it's worth considering how some common types of I/O might be speeded up.

As an example, consider text lines. These are lines of characters ending in \n while being manipulated in a program, and ending in \r\n or \n when residing in a disk file. \r\n is used with Microsoft system software on PCs, while \n is used on UNIX systems.

Suppose that we wish to read and write sets of text lines sequentially, for example, reading all the lines in a file, one after another, or writing to a file in a similar way.

One way to do this is illustrated in the following example. We set up our own file output buffer, and move characters from a passed-in String object directly to the buffer. We use the low-level FileOutputStream class to actually do the I/O; it has a write() method that takes a vector of bytes and outputs them to a file or to standard output.

We determine whether \r\n or just \n is needed on output, by looking at the system properties list. JDK 1.0 running on Windows NT has an anomaly or feature in the way that standard output is treated with respect to line delimiters, and so if output is to standard out it's treated differently.

This particular example, with the driver program below, runs about 5X faster than the equivalent using System.out.print(). The code for doing input of text lines is analogous.
        import java.io.*;

        class StdioOutput {
                private static final int BUFSIZ = 4096;
                private FileOutputStream fos;
                private byte buf[] = new byte[BUFSIZ];
                private int left;
                private int pos;
                private static boolean need_cr = false;

                // figure out whether we have \r or \r\n

                static {
                        String s = System.getProperty("line.separator");
                        need_cr = (s.length() >= 1 && s.charAt(0) == '\r');
                }

                // open a file

                public StdioOutput(String fn) throws IOException
                {
                        fos = new FileOutputStream(fn);
                        left = BUFSIZ;
                        pos = 0;
                }

                // open standard output

                public StdioOutput() throws IOException
                {
                        fos = new FileOutputStream(FileDescriptor.out);
                        left = BUFSIZ;
                        pos = 0;
                        need_cr = false;
                }

                // close a file

                public synchronized void close() throws IOException
                {
                        flush();
                        fos.close();
                        fos = null;
                }

                // flush output

                public synchronized void flush() throws IOException
                {
                        if (pos > 0)
                                fos.write(buf, 0, pos);
                        left = BUFSIZ;
                        pos = 0;
                }

                // output a character

                public synchronized void putc(int c) throws IOException
                {

                        // flush output buffer if needed

                        if (left <= 0)
                                flush();

                        // handle simple case

                        if (c != '\n' || !need_cr) {
                                left--;
                                buf[pos++] = (byte)c;
                        }

                        // handle \r\n

                        else {
                                left--;
                                buf[pos++] = '\r';
                                if (left <= 0)
                                        flush();
                                left--;
                                buf[pos++] = '\n';
                        }
                }

                // output a line

                public synchronized void putline(String s) throws IOException
                {
                        int len = (s == null ? 0 : s.length());

                        // empty string

                        if (len < 1)
                                return;

                        // whole string will fit in buffer

                        if (len + 1 <= left) {
                                if (len >= 2) {
                                        s.getBytes(0, len - 1, buf, pos);
                                        pos += len - 1;
                                        left -= len - 1;
                                }
                                putc(s.charAt(len - 1));
                        }

                        // whole string won't fit, do a character at a time

                        else {
                                for (int i = 0; i < len; i++)
                                        putc(s.charAt(i));
                        }
                }
        }

        public class testio2 {
                public static void main(String args[])
                {
                        StdioOutput fout = null;
                        String s;

                        try {
                                fout = new StdioOutput();
                        }
                        catch (Throwable e) {
                                System.err.println("* file opening error *");
                        }

                        try {
                                int N = 10000;
                                for (int i = 1; i <= N; i++)
        //                              System.out.print("xxxxxxxxxxxxxxx\n");
                                        fout.putline("xxxxxxxxxxxxxxx\n");
                        }
                        catch (Throwable e) {
                                System.err.println("*** file I/O error ***");
                        }

                        try {
                                fout.close();
                        }

                        catch (Throwable e) {
                                System.err.println("* file closing error *");
                        }
                }
        }



Managing Directories

In the last issue we illustrated a way of invoking subprograms from within Java, and gave an example of listing the contents of a directory using the command "ls". Someone pointed out that this example is a bit misleading, because "ls" is unique to UNIX systems and because there are portable Java library techniques for achieving the same end.

To illustrate this point, here is a bit of Java code that lists the contents of a directory in a portable way, using the java.io.File class:
        import java.io.*;

        public class dir {

                public static void main(String args[])
                {
                        File f = new File(".");

                        String lst[] = f.list();

                        for (int i = 0; i < lst.length; i++)
                                System.out.println(lst[i]);
                }

        }

We create a File object based on the current directory, and then retrieve a vector of Strings that name the files found in that directory.

The File class has methods for checking for the existence and attributes of a file, such as whether it can be read or written and whether it's a directory. It's also possible to filter the names returned by list() in an arbitrary user-defined way, using the FileNameFilter interface. File system attributes like filename and pathname separators are queryable as well.


Invoke Subprograms

It's often the case that when writing programs, you'd like to invoke another program from within the first and send it input and get output from it. How might this be done in Java? A simple example that runs the UNIX program "ls" to retrieve a listing of files in the current directory looks like this:
        import java.io.*;

        public class test1 {

                public static void main(String args[])
                {
                        try {
                                Process cp = Runtime.getRuntime().exec("ls");
                                DataInputStream d =
                                    new DataInputStream(cp.getInputStream());
                                String line = null;
                                while ((line = d.readLine()) != null)
                                        System.out.println(line);
                        }
                        catch (Throwable e) {
                        }
                }

        }

The call to exec() starts the process running, and returns a Process object. Then we query the Process object to get a buffered input stream that is connected to the output of the child process. We then can simply iterate over this stream, reading lines in turn, that are the output of ls. In a similar way, it's possible to send input to the child process or capture its error output.

The Process class has several additional methods available for controlling processes:
        waitFor()       wait for the child process to complete

        exitValue()     return the exit value for the process

        destroy()       kill the process

There is also a variant of exec() that supports passing of environment variables to the child process.


Enums

In C++ there is a facility known as enumerations or enumerated types that can be used to represent a set of integral values:
        enum Color {CO_R = 1, CO_G = 2, CO_B = 3};

After this declaration, Color can be used as a type (including for function overloading purposes), and values like CO_R can be used wherever integral constants would be used. Type checking is done, so that for example:
        Color c = CO_R;

        c = 37;

is invalid.

Java does not have enumerations. One way to simulate them is simply by defining constants in a class:
        public class Color {
                public static final byte CO_R = 1;
                public static final byte CO_G = 2;
                public static final byte CO_B = 3;
        };

public means that these values are accessible to all, static means that they're shared across all object instances, final means that they're immutable once set, and byte means that they're represented in bytes in the virtual Java machine.

Once a class like this is set up, you can say things like:
        byte b = Color.CO_G;

But notice that this simulation is only a simulation. There's nothing to stop me from saying:
        byte b = Color.CO_R;
        b = 29;

and thereby introduce an invalid color value. A solution to this problem would be to define Color as a "real" class, that represents the actual current value of an enumeration:
        public class Color {
                private byte value = 0;
                public static final byte CO_R = 1;
                public static final byte CO_G = 2;
                public static final byte CO_B = 3;
                public Color(byte b)
                {
                        if (b == CO_R || b == CO_G || b == CO_B)
                                value = b;
                        else
                                throw new IllegalArgumentException();
                }
                public byte getvalue()
                {
                        return value;
                }
        }

This approach works, but can be cumbersome to use. However, enums, like other language features, add to the total complexity and implementation cost of a language, and leaving them out of Java is a reasonable design tradeoff to make.


JavaDoc

You may have heard of the term "JavaDoc" in relation to Java. What is this? JavaDoc is a tool that comes with the Java Development Kit. It's used for preparing HTML (the language of the Web) documentation for Java classes. That is, it generates HTML code that documents a class or package (set of related classes), including their methods, interfaces, exceptions, class hierarchy, and so on.

JavaDoc extracts structured comments from a Java source file. For example:
        /**
         * Create a hash table.
         *
         * @param       sz initial table size in hash slots (>= 1)
         * @param       load_factor (average elements per slot)
         *              (0.25 - 10.0)
         * @param       growth_factor (how much to grow the table
         *              when load factor is exceeded) (1.1 - 2.0)
         * @exception   IllegalArgumentException for invalid arguments
         */

        public Hash(int sz, float load_factor, float growth_factor)
        {
                // stuff
        }

This is documentation for a constructor for a hash table class. JavaDoc keys off of "/**" and tags like "@param". Page 182 of David Flanagan's "Java in a Nutshell" (O'Reilly) describes the tags that can be used.

You can run JavaDoc on individual Java source files, or on whole packages. It generates a set of HTML files that can be used on a Web page or with a Web browser whether connected to the Web or not. The files have hyperlinks between related classes and methods, so that a user can navigate easily through the documentation.


Sorting

In the last issue we talked about sorting in Java and said that there's no approach to sorting that's really similar to qsort() in C/C++. A couple of people wrote to me about this and suggested a technique that does in fact have some similarities.

The idea is to write a sort method that accepts a Vector of Object references, along with a class object instance that is a wrapper for a compareTo() method as illustrated above. The sort routine will iterate over the vector and call out to the compare method to determine the ordering of any two objects. Specifically, this would look like:
        import java.util.Vector;

        // Orderable interface

        interface Orderable {
                // return < 0 if p1 < p2, 0 if equal, > 0 if p1 > p2
                public int compareTo(Object p1, Object p2);
        };

        // wrapper for a compareTo() method for ordering Strings

        class Ord implements Orderable {
                public int compareTo(Object p1, Object p2)
                {
                        return ((String)p1).compareTo(((String)p2));
                }
        }

        public class Sort {

                public static void sort(Vector v, Orderable or)
                {
                        if (v == null || or == null)
                                ; // give error of some kind

                        // get vector size

                        int n = v.size();

                        // sort

                        for (int i = 0; i < n - 1; i++) {
                                for (int j = i + 1; j < n; j++) {

                                        // do comparison

                                        if (or.compareTo(v.elementAt(i),
                                            v.elementAt(j)) > 0) {
                                                Object ti = v.elementAt(i);
                                                Object tj = v.elementAt(j);
                                                v.setElementAt(tj, i);
                                                v.setElementAt(ti, j);
                                        }
                                }
                        }
                }

                // driver program

                public static void main(String args[])
                {
                        Vector v = new Vector();
                        int N = 100;
                        int i = 0;

                        // add some strings

                        for (i = 0; i < N; i++)
                                v.addElement("xxx" + i);

                        // sort them

                        Sort.sort(v, new Ord());

                        // display the sorted list

                        for (i = 0; i < N; i++)
                                System.out.println((String)v.elementAt(i));
                }

        }

This code can be tuned in various ways (starting with the sort algorithm) but is illustrative of the technique. We can implement any sort strategy we want on a Vector of objects simply by changing the ordering method. For example, saying:
        class Ordrev implements Orderable {
                public int compareTo(Object p1, Object p2)
                {
                        return -((String)p1).compareTo(((String)p2));
                }
        }

        ...

        Sort.sort(v, new Ordrev());

will reverse the sorting order for Strings. In this particular example, Strings have a compareTo() method already defined, and we simply cast the Object references to Strings and call this method. Note that if the wrong compareTo() wrapper instance is used, then an illegal cast will be attempted, resulting in an exception being thrown. For example, the above case expects a String, and will generate an exception ("ClassCastException") if the objects we pass in are actually Integers. The "instanceof" operator can be used to help sort things out.

In production code we'd have Orderable defined in a separate source file. Ord might or might not be in its own file, depending on stylistic preferences. Ord is simply a wrapper for a particular compareTo() method. In C or C++ we would pass in a function pointer directly, but Java has no user-visible pointers and no global functions.

If we critique this approach and compare it to qsort(), there are some notable differences and some similarities:
        1.  This approach is higher-level than qsort(), because it
        doesn't fool with pointers and sizes of objects and so on.

        2.  This approach cannot be used to directly sort vectors of
        fundamental data types like int or double.  They must be
        sorted using object wrappers.

        3.  Both approaches require calling out to a function or
        method that is used to order elements.  Such calls are
        expensive.

        4.  This approach has some overhead in accessing and setting
        Vector element slots.  There is method call overhead, as well
        as the subscript range checking done each time within a method
        like elementAt().

It's possible to write a similar type of method for doing binary searches, kind of like bsearch() in the C library.


Protected/Default

In issue #004 we talked about public and private fields. When public is applied to a method or variable:
        public int x = 37;

        public void f() {}

it means that the method or variable is visible everywhere, while a private method or variable:
        private int x = 37;

        private void f() {}

is visible only within the class where it is defined.

Two other levels of visibility are protected:
        protected int x = 37;

and the default when no keyword is specified:
        void f() {}

These are identical except in one case. For both of these levels, the method or variable is visible in the class where it's defined and to subclasses and non-subclasses from the same package. For example:
        // file pack1/A.java

        package pack1;

        public class A {
                protected int x = 0;
                int f() {return x;}
                public static void main(String args[]) {}
        }

        class B extends A {
                void g()
                {
                        A p = new A();
                        int i = p.x + p.f();
                }
        }

        class C {
                void g()
                {
                        A p = new A();
                        int i = p.x + p.f();
                }
        }

while not being accessible from other packages:
        // file pack1/AA.java

        package pack1;

        public class AA {
                protected int x = 0;
                int f() {return x;}
                public static void main(String args[]) {}
        }

        // file pack2/BB.java

        package pack2;

        import pack1.AA;

        class BB extends AA {
                void g()
                {
                        AA p = new AA();
                        int i = p.x + p.f(); // error here
                }
        }

        class CC {
                void g()
                {
                        AA p = new AA();
                        int i = p.x + p.f(); // error here
                }
        }

Where protected and the default differ is in whether they are inherited by a subclass in a different package. For example:
        // file pack1/D.java

        package pack1;

        public class D {
                int x = 37;
                protected int y = 47;
                public static void main(String args[]) {}
        }

        // file pack2/E.java

        package pack2;

        import pack1.D;

        class E extends D {
                void f()
                {
                        int i = x;              // error here
                        int j = y;              // OK here
                }
        }

There are a couple more issues with packaging that we will explore in future issues.


Random Numbers

Random numbers are useful in many contexts in programming. One common use is in games, and another is for testing program algorithms such as those used in searching or sorting.

Java has random number support in the library class java.util.Random. Basic use is:
        import java.util.*;

        Random rn = new Random();

        ...

        int r = rn.nextInt();           // 32-bit random number

        double d = rn.nextDouble();     // random value in range 0.0 - 1.0

The random number generation algorithm is based on the linear congruential method (see for example Chapter 3 in Volume 2 of Knuth's "Art of Computer Programming"). This method has a starting value (a "seed"), and each value of the sequence is generated from the last. Whether such a stream of random numbers are in fact truly random is an interesting question beyond the scope of this discussion. If you're interested in that question you might consult Knuth's book.

If the default Random() constructor is used, the random number generator is seeded from the current time of day. You can also specify your own seed:
        Random rn = new Random(1234);

to generate a repeatable sequence of random numbers.

To show how random numbers might be used, here is a class Test that will generate random numbers in a range using the rand() method, or generate random strings composed of characters 'a' ... 'z'.
        import java.util.*;

        public class Test {

                private static Random rn = new Random();

                private Test()
                {
                }

                public static int rand(int lo, int hi)
                {
                        int n = hi - lo + 1;
                        int i = rn.nextInt() % n;
                        if (i < 0)
                                i = -i;
                        return lo + i;
                }

                public static String randomstring(int lo, int hi)
                {
                        int n = rand(lo, hi);
                        byte b[] = new byte[n];
                        for (int i = 0; i < n; i++)
                                b[i] = (byte)rand('a', 'z');
                        return new String(b, 0);
                }

                public static String randomstring()
                {
                        return randomstring(5, 25);
                }
        }

Actual random numbers are obtained using nextInt(), and then knocked down to the relevant range using the modulo ("%") operator.

Note that Test is simply a packaging vehicle with all of the methods as class methods ("static"). To obtain a random String of between 10 and 40 characters, you would say:
        String s = Test.randomstring(10, 40);



Public/Private

JAVA PROGRAM PACKAGING PART 2 - PUBLIC/PRIVATE

In the last issue we talked about CLASSPATH and Java packages. In this and subsequent issues, we'll be discussing visibility specifiers for instance (per object) and class (shared across all object instances) variables and methods.

The first two of these specifiers are "public" and "private". Specifying public means that the variable or method is accessible everywhere, and is inherited by any subclasses that extend from the class. "Everywhere" means subclasses of the class in question and other classes in the same or other packages. For example:
        // file A.java

        public class A {
                public void f() {}
                public static int x = 37;
        }

        // file B.java

        public class B {
                public static void main(String args[])
                {
                        A a = new A();
                        a.f();          // calls public method
                        A.x = -19;      // sets static variable in A
                }
        }

By contrast, "private" means that no other class anywhere can access a method or variable. For example:
        // file A.java

        public class A {
                private void f() {}
                private static int x = 37;
        }

        // file B.java

        public class B {
                public static void main(String args[])
                {
                        A a = new A();
                        a.f();          // illegal
                        A.x = -19;      // illegal
                }
        }

Private instance variables are not inherited. This means something slightly different in Java than in C++. In both languages private data members are in fact part of any derived class, but in Java the term "not inherited" in reference to private variables does double duty to mean "not accessible".

One crude way of figuring out just how much space an object instance requires is to use a technique like this one, where the amount of free memory is saved, many object instances are allocated, and then a calculation is done to determine the number of bytes per object instance:
        class x1 {
                private double d1;
                private double d2;
                private double d3;
                private double d4;
                private double d5;
        }

        public class x2 extends x1 {
                public static void main(String args[])
                {
                        int N = 10000;
                        x2 vec[] = new x2[N];

                        long start_mem = Runtime.getRuntime().freeMemory();

                        for (int i = 0; i < N; i++)
                                vec[i] = new x2();

                        long curr_mem = Runtime.getRuntime().freeMemory();

                        long m = (start_mem - curr_mem) / N;

                        System.out.println("memory used per object = " + m);
                }
        }

This technique is not without its pitfalls (notably issues related to garbage collection), but sometimes can provide useful information about object sizes.

In future issues we will be talking about other kinds of visibility, such as the default visibility level and "protected".


Global Variables and Functions

Java has no global variables or functions, unlike some other common languages. Every variable and function must be part of some class. Within a class, a variable or function ("method") may be a regular member of that class, or may be a class variable or class method.

Class methods and variables do not operate on particular object instances of a class. A class method is typically used as a utility function within the class, while a class variable is shared by all the instances of the class. For example, if you're writing a Date class to represent calendar dates, a class method might be used for the method that determines whether a given year is a leap year.

Using class methods and variables, it is possible to synthesize variables and methods somewhat similar to globals. For example, you can say:
        // file "Global.java"

        public final class Global {
                public static int x = 37;
                public static int f()
                {
                        return 47;
                }
        }

        // file "globtest.java"

        public class globtest {
                public static void main(String args[])
                {
                        int i = Global.x + Global.f();

                        System.out.println("i = " + i);

                        Global.x = 0;

                        System.out.println("x = " + Global.x);
                }
        }

"static" is the keyword used to denote that methods or variables are class ones. The Global class is declared as final, meaning that it cannot be extended (derived from). The class variable and method names in Global are denoted by prepending "Global." to them.

If we wanted to get a bit fancier, we could add a line to Global:
        private Global() {}

This declares a private constructor for Global, meaning that anyone who tries to create an object instance of Global will get an error message at compile time. The error will occur because they are trying to create an object whose constructor is inaccessible. This is reasonable behavior since we don't care about creating object instances of Global; it's just a wrapper for some variables and methods.

Another change that could be made would be to have all access to class variables in Global done through methods:
        private static int x = 47;
        public static void setx(int i) {x = i;}
        public static int getx() {return x;}

This makes it easy to trap all changes to the global variable.

A technique similar to that shown in this section is possible in C++, but is not required. C has no similar mechanism at all, though you can enforce your own rules via naming conventions and access functions.

It is possible to get into a long argument about the desirability of global variables. They are best avoided, except to track application-wide information, such as the current program state or resources that are to be shared between threads. The Java I/O system uses the technique illustrated here to set up System.out for use in code like:
        System.out.println("xxx");

System is a final class with a private constructor, and out is a class variable defined within that class.


Linked List

Java has no user-visible pointers, so how would you do a linked list?
Each element needs to point to the next one, doesn't it?

Well, fortunately there is a way, illustrated using this brief example:
        // source file link.java

        public class link {
                public int value;              // value of element
                public link next;              // reference to next

                // constructor
                public link(int n, link ln)
                {
                        value = n;
                        next = ln;
                }

                public static void main(String args[])
                {
                        // initialize list head
                        link head = null;

                        // add some entries to list
                        for (int i = 1; i <= 10; i++)
                                head = new link(i, head);

                        // dump out entries
                        link p = head;
                        while (p != null) {
                                System.out.println(p.value);
                                p = p.next;
                        }
                }
        }

Java does not have pointers, but instead uses implicit references. A line like:
        link next;

does not actually declare an object instance of class link, but rather a reference to an object instance. The line:
        head = new link(i, head);

creates a new element for the list, sets its value, and then sets the object reference in "next" to point at the old list head (this approach means that the last element inserted will be at the head of the list). Saying:
        p = p.next;

simply picks up the "next" reference from the current node and makes it the current element being worked on.

When we're done using this list, we can simply leave the list with all its elements lying around -- garbage collection will eventually reclaim it.

If what you really want to do in a Java application is manage a list of numbers, there are higher-level techniques in the language and library (such as the Vector class) for doing so more cleanly than the approach illustrated here. This example shows some of what can be done underneath to implement the higher-level schemes.


Chars, Unicode and File I/O

In the last section we mentioned that a char in Java is 16 bits, stored as two bytes. The high byte typically is 0, and various of the Java library classes and methods allow one to specify the high byte. Here's an example of byte and character I/O that illustrates some of these points, in a file "uni.java":
        import java.io.*;

        public class uni {
                public static void main(String args[])
                {
                        InputStream istr = null;

                        try {
                                istr = new FileInputStream("testfile");
                        }
                        catch (FileNotFoundException e) {
                                System.err.println("*** file not found ***");
                                System.exit(1);
                        }

                        try {
                                int b;
                                String s = "";
                                while ((b = istr.read()) != -1) {
                                        s += (char)b;
                                }
                                System.out.print(s);
                        }
                        catch (IOException e) {
                        }

                        System.exit(0);
                }
        }

In this example, we attempt to open a file input stream to an input file "testfile", catching an exception and bailing out if the open fails (more about exceptions below). Note that we don't close the file explicitly. This is done by something akin to a C++ destructor, a method called finalize() that is invoked when garbage collection is done. We will talk about this area at some point; the semantics of resource cleanup and freeing are different in Java because of delayed object destruction.

Then we read bytes from the file using the read() method. The bytes are returned as ints, so that -1 can be used to indicate end of file (C has a similar trick with EOF). We take each int (byte) and cast it to a character and append it to a String object that we'd initialized to the empty string. Finally, we print the string.

A String object has a sequence of characters in it, and we have converted the input bytes that were read into characters and shoved them into the string. Since characters are Unicode, we have converted a sequence of input bytes into Unicode.

But it's not quite this easy. In casting to a character, there is the implicit supplying of a 0 to fill the high byte of the character, resulting in code that's not very portable. A better way to express the line:
        s += (char)b;

would be:
        byte x[] = {(byte)b};
        s += new String(x, 0);

In other words, build a vector of bytes and construct a String from them, with the high byte fill value explicitly specified.

We will be saying more about Java I/O in the future. The Java library has a variety of classes and methods for dealing with input and output of various types. The I/O example shown above illustrates a way of doing low-level input. There are higher-level mechanisms available in the library.

A WAY OF DOING CLASS INITIALIZATION

In C++ one can use constructors to initialize class object instances when they're created, and employ static data members that are initialized when the program starts. But what if you'd like some code to be executed once for a given class, to kind of set things up for the class? One way of doing this in Java is to say:
        public class A {
                static {
                        System.out.println("got to static initialization");
                }
                public static void main(String args[])
                {
                        System.out.println("start of main()");
                        A c1 = new A();
                        A c2 = new A();
                }
        }

No matter how many instances of objects of class A are created, the block of code at the top will be executed only one time. It serves as a hook to do class setup at the beginning of execution.

WHAT HAPPENS WHEN YOU OUTPUT A CHARACTER?

The technique shown in the previous section has one very important use. When you say:
        System.out.println("x");

what happens? It's interesting to trace through the sequence of operations used to output a character.

In the first place, System is a class defined in the Java library. It is a wrapper class that you do not actually create object instances of, nor may you derive from the System class, because it is declared as "final". In C++ such a class is sometimes referred to as a "static global class".

System.out is defined as:
        public static PrintStream out;

meaning that it's available to all and that there is only one object instance of PrintStream for "out". This PrintStream stream corresponds to standard output, kind of like file descriptor 1 in UNIX, stdout in C, or cout in C++. Similar streams are established for input and standard error output.

The output stream is initialized via a static initialization block of the type illustrated above. The actual code is:
        out = new
		      PrintStream( new BufferedOutputStream( new FileOutputStream( FileDescriptor.out ), 128 ), true );

This is a mouthful that says that a PrintStream is based on a BufferedOutputStream (with a buffer 128 long) which is based on a FileOutputStream with a specified file descriptor, and that output is line buffered.

Saying:
        System.out.println("xxx");

means that you're invoking the println(String) method for a PrintStream. Doing so immediately results in the sequence:
        PrintStream.print("xxx");
        PrintStream.write('\n');

PrintStream.print("xxx") contains a loop that iterates over the characters in the String ("xxx" is a String, not a vector of characters) calling PrintStream.write() for each. PrintStream.write() calls out.write(), implementing line buffering as it goes.

What is out.write()? When the output stream was initialized, we created a PrintStream object and said that it should be based on a BufferedOutputStream. "out" is an instance variable of a class FilterOutputStream from which PrintStream derives ("extends"), and out is set to reference a BufferedOutputStream object. In a similar way, BufferedOutputStream is based on FileOutputStream.

out.write() in BufferedOutputStream collects characters into a buffer (specified in the creation line illustrated above). When the buffer becomes full, out.flush() is called. This results in a different write() being called in the FileOutputStream package. It writes a sequence of bytes to the file descriptor specified when the stream was created. This last method is native, that is, is implemented in C or assembly language and not in Java code itself.

This approach to I/O is quite flexible and powerful, and names like "stream nesting" and "stream filtering" are used to describe it. It's not a terribly efficient approach, however, especially since Java itself is interpreted and many of the higher layers of the system are written in Java itself.

One other note: when trying to figure out just what methods are called in an example like the one in this section, it's helpful to use the profiling feature of JDK:
        $ java -prof xxx

This shows called methods, who called them, and how many times they were called.

AN ANNOTATED EXAMPLE OF JAVA USAGE

Here is a longer example of a complete Java program (not an applet). This program does simple expression evaluation, so for example, input of:
        (1 + 2) * (3 + 4)

yields a value of 21.

If you're not familiar with this sort of programming, similar to what is found in language compilers themselves, a brief explanation is in order. The program takes input and splits it into what are called tokens, logical chunks of input. For the input above, the tokens are:
        (
        1
        +
        2
        )
        *
        (
        3
        +
        4
        )

and the white space is elided. Then the program tries to make sense of the stream of input tokens. It implicitly applies a grammar:
        expr -> term | expr [+-] term
        term -> fact | term [*/] fact
        fact -> number | ( expr )

Don't worry too much if you don't understand this. It's a way of describing the structure of input. You can think of it as a way of converting an input expression into the Reverse Polish Notation that some older calculators used to use.

Here is the actual program, in a file "calc.java". We will have more to say about this program in the next section below. Annotations are given in /* */ comments, while regular program comments use //. (Note: we're not trying to do anything fancy with comments for JavaDoc purposes, a subject to be presented another time).
        import java.io.*;

        public class calc {
                private String in_line;                 // input line
                private int in_len;                     // input line length
                private int currpos;                    // position in line
        		// The input line, its length, and the current position in it.
                private byte curr_tok;                  // current token
                private int val_token;                  // value if num
        		// The current token and its value if it's a number.
                private boolean had_err;                // error in parsing
        		// Used to record whether a parsing error occurred on the input.
        		// Exception handling could also be used for this purpose, and is used for another type of error (divide by 0).
                private static final byte T_NUM = 1;    // token values
                private static final byte T_LP = 2;
                private static final byte T_RP = 3;
                private static final byte T_PLUS = 4;
                private static final byte T_MINUS = 5;
                private static final byte T_MUL = 6;
                private static final byte T_DIV = 7;
                private static final byte T_EOF = 8;
                private static final byte T_BAD = 9;
        /*
        Possible token values.  These are private (available only to the
        class), static (shared across all class object instances), and
        final (constant).
        */
                // get next token from input line
                private void get_token()
                {
                        // skip whitespace

                        while (currpos < in_len) {
                                char cc = in_line.charAt(currpos);
								// in_line.charAt(currpos) returns the current character from the string.
                                if (cc != ' ' && cc != '\t')
                                        break;
                                currpos++;
                        }

                        // at end of line?

                        if (currpos >= in_len) {
                                curr_tok = T_EOF;
                                return;
                        }

                        // grab token

                        char cc = in_line.charAt(currpos);
                        currpos++;
                        if (cc == '+' || cc == '-')
                                curr_tok = (cc == '+' ? T_PLUS : T_MINUS);
                        else if (cc == '*' || cc == '/')
                                curr_tok = (cc == '*' ? T_MUL : T_DIV);
                        else if (cc == '(' || cc == ')')
                                curr_tok = (cc == '(' ? T_LP : T_RP);
        /*
        This block of code could also be handled via a switch statement
        or in a couple of other ways.
        */
                        else if (Character.isDigit(cc)) {
                                int n = Character.digit(cc, 10);
                                while (currpos < in_len) {
                                        cc = in_line.charAt(currpos);
                                        if (!Character.isDigit(cc))
                                                break;
                                        currpos++;
                                        n = n * 10 + Character.digit(cc, 10);
                                }
                                val_token = n;
                                curr_tok = T_NUM;
        /*
        The above code grabs a number.  Character.isDigit(char) is a method
        of the character class that returns a boolean if the character is a
        digit.  Character.digit(char, int) converts a character to a number
        for a given number base (10 in this case).

        The primitive types like char have corresponding class types, though
        you cannot call a method directly on a primitive type object.  You
        must instead use the techniques illustrated here.
        */
                        }
                        else {
                                curr_tok = T_BAD;
                        }
        		// The case where the token can't be recognized.
                }

                // constructor, used to set up the input line
                public calc(String s)
                {
                        in_line = s;
                        in_len = in_line.length();
                        currpos = 0;
                        had_err = false;
                        get_token();
                }
        /*
        The constructor sets up an object instance for doing calculations.  We
        set up the input line, clear any error condition, and grab the first
        token.
        */
                // addition and subtraction
                private double expr()
                {
                        // get first term

                        double d = term();

                        // additional terms?

                        while (curr_tok == T_PLUS || curr_tok == T_MINUS) {
                                byte t = curr_tok;
                                get_token();
                                if (t == T_PLUS)
                                        d += term();
                                else
                                        d -= term();
                        }
                        return d;
                }
        /*
        This and the next method are similar.  They grab a term() or fact()
        and then check to see if there are more of them.  This matches input
        like:

                1 + 2 + 3 + 4 ...

        As each token is consumed, another one is grabbed.
        */
                // multiplication and division
                private double term()
                {
                        // get first factor

                        double d = fact();

                        // additional factors?

                        while (curr_tok == T_MUL || curr_tok == T_DIV) {
                                byte t = curr_tok;
                                get_token();
                                if (t == T_MUL)
                                        d *= fact();
                                else {
                                        double d2 = fact();
                                        if (d2 == 0.0 && !had_err)
                                               throw new ArithmeticException();
                                        d /= d2;
        /*
        This code is similar to expr() above but we check for division by 0
        and throw an arithmetic exception if we find it.  We will see below
        where this exception is handled.
        */
                                }
                        }
                        return d;
                }

                // numbers and parentheses
                private double fact()
                {
                        double d;

                        // numbers
                        if (curr_tok == T_NUM) {
                                d = val_token;
                                get_token();
                        }
        				//  If a number, retrieve the value stored in val_token.
                        // parentheses
                        else if (curr_tok == T_LP) {
                                get_token();
                                d = expr();
                                if (curr_tok != T_RP) {
                                        had_err = true;
                                        d = 0.0;
                                }
                                get_token();
                        }
        /*
        If (, then grab the expression inside and check for ).  If not found,
        record that we had an error.  We could also throw an exception at this
        point.
        */
                        // garbage

                        else {
                                had_err = true;
                                get_token();
                                d = 0.0;
                        }
        				// The token was not recognized, so we have bad input.
                        return d;
                }

                // parse input and get and print value
                public String get_value()
                {
                        double d;

                        try {
                                d = expr();
                        }
                        catch (ArithmeticException ae) {
                                return new String("*** divide by 0 ***");
                        }
                        if (had_err || curr_tok != T_EOF)
                                return new String("*** syntax error ***");
                        else
                                return String.valueOf(d);
        /*
        Here is where we actually try to get the value of the expression.  We
        convert its value back to a String for reasons of flexibility in
        handling error conditions.

        Division by 0 will result in an exception being thrown and caught
        here.

        If we encountered an error, or if we've not exhausted the input string
        (for example, for input "((0)))"), then we also flag an error.
        Otherwise, we return the string value of the double using the method
        String.valueOf(double).
        */
                }


                // get a line of input from the keyboard
                private static String getline()
                {
                        DataInput di = new DataInputStream(System.in);
                        String inp;

                        try {
                                inp = di.readLine();
                        }
                        catch (IOException ignored) {
                                inp = null;
                        }
                        // This is a wrapper function to get a line of input from the keyboard.
                        return inp;
                }

                // driver
                public static void main(String args[])
                {
                        String inp = "";

                        // command line arguments

                        if (args.length > 0) {
                                for (int i = 0; i < args.length; i++)
                                        inp = inp + args[i];
                                calc c = new calc(inp);
                                System.out.println(c.get_value());
        /*
        If there are command-line arguments, we will append them into one
        string using the "+" operator and then evaluate the value of the
        expression. args.length is the number of command-line arguments, and
        args[i] is the i-th argument.

        The line:

                calc c = new calc(inp);

        creates a new calc object and calls its constructor with inp as the
        String argument to the constructor.

        c.get_value() returns the expression value as a String.
        */
                        }

                        // no command line arguments, prompt user
                        else {
                                for (;;) {
                                        System.out.print("input string: ");
                                        System.out.flush();
        								// We flush output here because it's normally line buffered and we've not
        								// output a newline character.
                                        inp = getline();
                                        if (inp == null)
                                                break;
        								// End of input.
                                        calc c = new calc(inp);
                                        System.out.println(c.get_value());
                                }
                        }
                }
        }


Security


Virtual Machine

If you've paid much attention at all to coverage of Java in the technical and business press, you might have encountered discussions on Java security, and read about malicious applets and so on. This is an interesting and complex subject to contemplate, and we'll spend several issues looking at it. There are multiple types and levels of security to consider.

Let's first look at a short C program:
        void main()
        {
                char c;
                int fd;
                char* p = 0;
                char buf[1024];

                fd = open("data", 0);
                while (read(fd, &c, 1) == 1)
                        *p++ = c;
                close(fd);
        }

This is a mixture of C code and UNIX system calls. It opens a data file, reads from it, and places the read characters into a character buffer.

Unfortunately, this program has a bug, in that the statement:
        p = buf;

was omitted. So the program will write via the pointer p, but p is null (0), and the bytes will get written into memory location 0 and succeeding locations. If you're running on a DOS machine, this will probably silently crash your computer. If you are on a UNIX machine with virtual address space starting at 0, with text (the program itself) in low memory, then perhaps you'll get a segmentation violation error.

The point is that a language like C is "close" to the hardware. This is literally true if running with DOS, somewhat less true if using virtual memory and process protection. Direct use of system calls for doing I/O in the example above is another illustration of this point. This feature of C is both a strength and a weakness. For example, C is a good language for writing I/O device drivers, because it's close to the hardware.

Java takes a different tack. It has no user-visible pointers, and no notion of an explicit memory address that you can manipulate or print out. With Java it's still possible to make the dumb mistake found in the example above, but the result would simply be an immediate null pointer exception. There is no way to write a byte to physical or virtual address 0.

Similarly, there is no direct access to system calls. A Java program can certainly do I/O to disk files (applets are restricted in this area), but it can't make system calls itself.

This insulation from the physical machine, via the Java Virtual Machine, means that program execution tends to be safer as far as program crashes and interference with other programs go. The tradeoff is inability to control the actual physical machine, and some loss of efficiency. This implies that Java is suited for a somewhat different set of applications than a language like C, though of course there is much overlap between the two.


Verification

We saw last time how the Java virtual machine model insulates a Java program from hardware, and how this is both desirable and undesirable.

In this issue we'll talk a bit about verification, another aspect of security. A java class is compiled into platform-independent ".class" files containing byte streams. These streams contain the actual program codes (bytecodes, constants, debugging information, and so on).

Suppose that I have the hello program:
        public class hello {

                public static void main(String args[])
                {
                        System.out.println("Hello World");
                }

        }

and I compile it:

        $ javac hello.java

resulting in a "hello.class" file of 461 bytes (in JDK 1.1). And I'm feeling malicious and decide to tweak one of the bytes:
        import java.io.*;

        public class tweak {
                public static void main(String args[])
                {
                        int offset = Integer.parseInt(args[0]);
                        try {
                                RandomAccessFile raf =
                                    new RandomAccessFile("hello.class", "rw");
                                raf.seek(offset);
                                raf.writeByte(97);
                                raf.close();
                        }
                        catch (Throwable e) {
                                System.err.println("*** exception ***");
                        }
                }

        }

by saying:
        $ javac tweak.java
        $ java tweak 0

thereby writing the byte "97" into location 0 in "hello.class". Obviously, this is not how Java was intended to be used. What will happen?

It turns out that the first four bytes of a Java .class file must be the hex value "0xCAFEBABE", and writing 0 into one of these will invalidate the file. If I then try to run the program:
        $ java hello

I will get an immediate error. This particular feature is fairly common in program binaries and often goes by the name of "magic number".

When a Java program is run, the interpreter first invokes the Java Verifier. The Verifier checks the magic number along with other properties of the .class file, including:

There are many other checks done by the Verifier on .class files. This list is merely illustrative. Verifying a class not only improves security, but speeds up actual bytecode interpretation because the checks don't have to be repeated.

An interesting book that goes into detail on Java security is "Java Security - Hostile Applets, Holes, and Antidotes", by Gary McGraw and Edward Felten, published 1997 by Wiley for $20.

We will be discussing security further in future issues.


The Security Manager

In previous issues we've looked at how the Java programming model insulates one from hardware, and how various types of checks are performed before executing a Java program.

In this issue we'll look at the Java security manager. This is a class in java.lang, that can be used to impose a specific security policy on running Java programs (including applets loaded by a Web browser).

The SecurityManager class contains a set of methods, such as checkRead(), that are called by Java core libraries. In a standalone program, typically no security manager is installed, and the program is wide open as to what it's allowed to do. An instance of a SecurityManager class object can be installed exactly once, after which the security policies in force are those of the class instance. By default, nothing is allowed, and so a class derived from SecurityManager needs to override some of the methods.

Let's see how this works in practice. There is a method:
        public void checkRead(String file)
        {
                throw new SecurityException();
        }

found in SecurityManager. We can override this method as follows:
        import java.io.*;

        class test_SecurityManager extends SecurityManager {
                public void checkRead(String file)
                {
                        if (file.charAt(0) == '/')
                                throw new SecurityException();
                }
        }

        public class Security {
                public static void main(String args[])
                {
                        System.setSecurityManager(new test_SecurityManager());
                        try {
                                FileInputStream fis =
                                    new FileInputStream("/xxx");
                                fis.read();
                                fis.close();
                        }
                        catch (SecurityException e) {
                                System.err.println("security exception");
                        }
                        catch (IOException e) {
                                System.err.println("I/O exception");
                        }
                        catch (Throwable e) {
                                System.err.println("some other exception");
                        }
                }
        }

to check the pathnames of files that an application accesses. In this example, we check that the pathname does not start with "/". If it does, we throw a SecurityException.

As we said, a security manager can only be established once in a given session (if this was not so, the old manager could simply be replaced by a more liberal one). By default, the check methods disallow operations, so an overriding method will typically relax some restriction. A Web browser may or may not have mechanisms for changing the default security policies it imposes on applets that are downloaded and executed.

This particular example does not work correctly with JDK 1.1 running on Windows 3.51, and requires JDK 1.1.1.


Class Loaders

In previous issues we've looked at some of the aspects of Java security, namely Java's use of an abstract machine removed somewhat from the hardware, verification of loaded code, and the security manager.

Another aspect of security centers around class loaders. A class loader in Java is responsible for converting a stream of bytes (for example, as found in a .class file) into a class known to the Java runtime system. When you run a Java standalone program, there is a built-in loader that converts .class files into a running program. Similarly, when an applet is downloaded from the Web, there is a loader responsible for installing it on your system.

Other languages such as C also have the notion of loading, where a binary image is turned into an executing program. But with Java there are some security aspects with loading as well.

For example, suppose that I have malicious intentions, and I decide to fool the Java system by trying to override one of the core classes, java.lang.Runtime, supplying my own version. How is this prevented by the class loader?

One way this is done is by keeping local built-in classes distinct from those loaded from a URL somewhere on the Web, that is, keeping classes in separate name spaces, according to where they were loaded from. When a class is looked up, the local name space can be checked first.

There's more we could say about class loaders, but this brief introduction gives an idea of what is going on behind the scenes.


Denial of Service

Another type of security problem in Java, that's a bit different from what we've already looked at in this series, is denial of service. This involves an applet that meets the security requirements previously discussed, but which essentially takes over the user's whole machine and prevents anything else from being done. That is, I as a user am surfing the Web, and come to a page with a Java applet call embedded in the HTML. The applet starts up and prevents me from using my computer for any other function.

A couple of examples of such applets would be one that does some sort of number crunching function, or one that creates a large number of very large windows. McGraw and Felten's "Java Security" book gives some examples of such applets, which we won't reproduce here.

The simplest solution to this type of problem is to by default disable your Web browser's ability to execute Java applets, and only selectively enable this feature for Web sites that you trust. Another possible longer-term approach would involve being able to establish some sort of resource usage limits.