Java Newsletter by Glen McCluskey - Issue 3
Issue #003
April, 1996
Contents
- Global Variables and Functions in Java
- How to Do Linked Lists in Java
- Comparing C/C++ With Java Part 3 - Operator Overloading
- Introduction to Applet Programming Part 1 - Handshaking Protocols
- Java Program Packaging Part 1 - CLASSPATH and Packages
INTRODUCTION
In this issue we'll talk about using global variables in Java programs, show a simple technique for using linked lists, talk about operator overloading, start a series about writing Java applets, and finally present the first part of a series on how Java programs are packaged.
GLOBAL VARIABLES AND FUNCTIONS IN JAVA
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.
HOW TO DO LINKED LISTS IN JAVA
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.
COMPARING C/C++ WITH JAVA PART 3 - OPERATOR OVERLOADING
In C++, code like this:
class A {
public:
void operator+=(int);
};
void f()
{
A a;
a += 187;
}
is legal. The operator "+=" has been overloaded to have a special meaning when its first operand is a class object. This usage is equivalent to:
a.operator+=(187);
that is, is a member function call on the object instance.
Java does not have operator overloading. But you may have seen sequences like:
public class test1 {
public static void main(String args[])
{
String s1 = "abc";
s1 += "def";
}
}
which are perfectly legal. Given that in C and C++ "+=" only works on arithmetic or pointer types, and Java has no operator overloading, why is this legal?
The answer is that in Java, the String type is part of the language proper. String methods are defined in libraries, just as in other languages, but Strings are actually in the language itself. When += is encountered, a call to the proper method is substituted. We can verify this by disassembling the compiled bytecodes:
$ javac test1.java
$ javap -c test1
This results in:
Method void main(java.lang.String [])
0 ldc #2 <String "abc">
2 astore_1
3 aload_1
4 new #3 <Class java.lang.StringBuffer>
7 dup
8 invokenonvirtual #8 <Method java.lang.StringBuffer. \
<init>()V>
11 swap
12 invokevirtual #9 <Method java.lang.StringBuffer.append \
(Ljava/lang/String;)Ljava/lang/StringBuffer;>
15 ldc #1 <String "def">
>>>>> 17 invokevirtual #9 <Method java.lang.StringBuffer.append \
(Ljava/lang/String;)Ljava/lang/StringBuffer;>
20 invokevirtual #6 <Method java.lang.StringBuffer.toString \
()Ljava/lang/String;>
23 astore_1
24 return
A bit of light reading! From studying the bytecodes, we can tell what actually happens when += is used.
Operator overloading is one of the more requested new features for Java. Adding it would yield expressiveness and notational convenience to the language, at the cost of making it harder to read and follow code. It's quite easy in C++ to stumble across expressions using overloaded operators, where the expression usage is so obscure that it is very difficult to understand what is going on. The most extreme case of operator usage is the language APL, famous for its one-line programs.
INTRODUCTION TO APPLET PROGRAMMING PART 1 - HANDSHAKING PROTOCOLS
In newsletter #001 we talked about applets a bit, and presented a simple example of one. A Java applet is not a standalone program but one that executes in a specific context, that of a Web browser. Before we get into the details of applet programming in future issues, it might be worth illustrating some of the handshaking that goes on between an applet and its controlling environment. Normally an applet is executed by a browser, though in the Java Development Kit there's also a tool called "appletviewer" for this purpose.
But in our presentation here, we're going to present an applet with an attitude -- an applet with a main() method that shows what happens when an applet is executed. The annotated code is as follows:
// source file ga.java
import java.awt.*;
// An applet normally runs within an instance of a Frame, a
// windowing object. We subclass Frame so that we can trap
// frame events, notably the event that kills a frame. All
// other events are passed to the superclass.
class AppFrame extends Frame {
// constructor - pass to superclass
public AppFrame(String s)
{
super(s);
}
// handle a frame event
public boolean handleEvent(Event e)
{
// see if they want to destroy window
if (e.id == Event.WINDOW_DESTROY) {
System.exit(0);
return true;
}
// pass to superclass if not handled
return super.handleEvent(e);
}
}
// Here we extend the standard applet class in the Java
// library. This particular applet graphs the function
// y = x^2.
public class ga extends java.applet.Applet {
// Y = X*X function
private int f(int x)
{
// For reasons of personal taste we move the X,Y
// origin from the upper left to the lower left
// of the frame. We have to retrieve the height
// and width each time because they may change due
// to frame resizing.
int h = size().height;
int w = size().width;
int w2 = (w - 1) * (w - 1);
double perc = (double)(x * x) / w2;
int amount = (int)(h * perc + 0.5);
return h - amount;
}
// The paint() method is called whenever the frame
// needs to be updated.
// paint method
public void paint(Graphics g)
{
int w = size().width;
// We iterate across the frame width, computing
// a Y for each X value and drawing it.
for (int x = 0; x < w; x++)
g.drawLine(x, f(x), x + 1, f(x + 1));
}
public static void main(String args[])
{
// We create an object instance of our applet.
// generate an instance of the applet
ga g = new ga();
// We init and start the applet. init() and start() are
// methods that are found in the superclass. They may be
// overridden or not as desired by an applet.
// start it
g.init();
g.start();
// Here we assign the applet to a frame, within which
// it will run.
// put that applet in a frame
AppFrame f = new AppFrame("Test");
f.add("Center", g);
f.resize(400, 400);
// show the frame contents
// We display the frame contents.
f.show();
}
}
The contents of main() illustrate the protocol used by a browser. An applet has certain methods, such as init() and start(), that constitute its interface with the browser.
JAVA PROGRAM PACKAGING PART 1 - 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());
}
}
ACKNOWLEDGEMENTS
Thanks to Thierry Ciot, Irv Kanode, Mike Paluka, and Alan Saldanha for help with proofreading.
SUBSCRIPTION INFORMATION / BACK ISSUES
To subscribe to the newsletter, send mail to majordomo@world.std.com with this line as its message body:
subscribe java_letter
Back issues are available via FTP from:
rmii.com /pub2/glenm/javalett
or on the Web at:
There is also a C++ newsletter. To subscribe to it, say:
subscribe c_plus_plus
using the same majordomo@world.std.com address.
-------------------------
Copyright (c) 1996 Glen McCluskey. All Rights Reserved.
This newsletter may be further distributed provided that it is copied in its entirety, including the newsletter number at the top and the copyright and contact information at the bottom.
Glen McCluskey & Associates
Professional Computer Consulting
Internet: glenm@glenmccl.com
Phone: (800) 722-1613 or (970) 490-2462
Fax: (970) 490-2463
FTP: rmii.com /pub2/glenm/javalett (for back issues)
Web: http://www.rmii.com/~glenm