Issue #001 February, 1996 Contents: Introduction The Hello Program in C, C++, and Java Java Compilation and Execution Comparing C/C++ with Java Part 1 - Garbage Collection The Simplest Applet Performance INTRODUCTION This newsletter will be devoted to the Java programming language, with tutorial information about its features, program and applet examples, comparisons with C and C++, and so on. There will be less emphasis on the HotJava browser and on particular Java tools and environments. Java is intended to be highly portable, but a mention should be made of the environment used for the examples presented in the newsletter. The Java Development Kit 1.0 from Sun will be used, running on Windows NT 3.51 on a 75 MHz Pentium PC. JDK 1.0 is available for downloading from Sun, and may differ in some respects from the beta versions of the JDK found on CD-ROMs bundled with popular Java books. Speaking of books, the newsletter will not have regular book reviews, but one excellent book deserving of mention is the Lemay & Perkins one "Teach Yourself Java in 21 Days" (Sams.net/Macmillan, $40). It's got 500 pages of information on Java the language, with many examples and a CD-ROM included. It's a good book for learning Java. If you have suggestions or comments about the content of the newsletter, please direct them to the address given below. THE HELLO PROGRAM IN C, C++, AND JAVA One way to start to understand the different approach that Java takes is to consider the hello program written in three different languages. In C, we have: #include main() { printf("Hello, world\n"); return 0; } In C++, the equivalent would be: #include main() { cout << "Hello, world\n"; return 0; } In Java, the program would be: public class hello { public static void main(String args[]) { System.out.println("Hello, world"); System.exit(0); } } The C and C++ versions are similar, but Java is noticeably different. Some of the differences are as follows. 1. Java has no header files. 2. Java "knows" about certain system features like the String class and the output stream System.out. 3. There are no global functions as in C and C++, but instead every function must be a part (a "method") of some class. This includes main(). 4. main() is a method ("member function" in C++) of the hello class, but it does not operate specifically on object instances of that class, and instead is what is called a "class method" that is part of the class for packaging purposes. In C++ the equivalent would be a static member function. 5. The hello class must be defined in a file called hello.java. There is no equivalent requirement in C or C++. 6. To pass out an exit status from the program, Java uses System.exit(n). In C and C++, there is the library function exit(), or one can return a value from main(). The Java approach is different from some other popular languages. Whether it is "right" is a somewhat pointless thing to argue about. For example, there are good reasons for not having a programming language "know" about I/O facilities such as stream I/O, but also good reasons on the other side. Once you have this program in a file, how would you compile and execute it? Using Sun's JDK 1.0, the steps would be: $ javac hello.java $ java hello Running the Java compiler causes a file hello.class to be produced. This file is a compiled form of the original program, consisting of a sequence of what are called bytecodes. The interpreter is then called and given the name of a class to be interpreted. JAVA COMPILATION AND EXECUTION The Java compiler takes source code and converts it into an intermediate form known as bytecodes. These codes are independent of any machine language for a given processor such as the Intel 80x86 series or the Sun Sparc or the DEC Alpha. Rather, they are interpreted by the Java interpreter, that is, the interpreter looks at each bytecode and decides what to do with it. The Java interpreter must be ported to each hardware platform, but the bytecodes are portable across platforms. This means that Java programs are portable across many platforms, and that a compiled program like hello.class can be executed on different hardware than it was compiled on. The basic technology for generating bytecodes is not new. SmallTalk has used it for some years, and there was a version of Pascal in the 1970s that generated something called P-code that was similar to bytecodes. We will say more about this approach and its performance characteristics later. COMPARING C/C++ WITH JAVA - PART 1 In this section we will be discussing some of the differences between C/C++ and Java. One of the big differences that a C/C++ programmer will notice right away is that Java has no malloc/free operators (C), or delete operator (C++). Instead, it uses a technique known as garbage collection. The way garbage collection works is simply to clean up after you, by keeping track of all allocated blocks of storage and then reclaiming them when they're no longer needed. For example, with this code fragment: public class hello { static void f() { int x[] = new int[1000]; /* stuff */ } static void g() { } public static void main(String args[]) { f(); g(); } } After the call to the f() method returns, the dynamically allocated x[] vector is garbage and is a candidate for reclaiming. Whether it is in fact reclaimed immediately is open to question; typically a garbage collector will start up only when it's needed, that is, when free memory is low. It's also possible to force garbage collection to be done at arbitrary points if desired. What are the tradeoffs with using garbage collection? The big advantage is that it relieves the programmer of the chore of managing dynamic storage. Problems with memory leaks, freeing an allocated block twice (often resulting in a program crash), and so on are a thing of the past. On the negative side, garbage collection will be slower than the C/C++ approach to storage management, especially if one takes advantage of the C++ techniques for defining specialized storage managers on a per-class basis. And when the actual garbage collector starts up, there may be a significant time hit as it goes through and reclaims all the storage no longer being used. Also, the runtime support within the Java interpreter will be more complex, because of the need to support the garbage collection facility. So we might say that the Java memory management facilities are at a higher level than those in C or C++. This is an advantage for nearly all programs, while causing difficulties for a few applications of the real-time and embedded variety. THE SIMPLEST APPLET You may have heard the term "applet" in relation to Java and Web programming. What is an applet? Here is a very simple example of one, in a file called "applet.java": import java.awt.*; public class applet extends java.applet.Applet { public void paint(Graphics g) { g.drawString("Hello world!", 25, 25); } } This is not a standalone Java program (there is no main() method, for example). Rather, it's a program that must be run in a special context. There is a class called java.applet.Applet in the Java library and the class shown here inherits or derives ("extends") from it. To execute this program, one needs to write some HTML (Hypertext Markup Language, used in Web programming), in a file called "applet.html": Hello World Example Applet The heart of this HTML is the line reading: This line points at the compiled Java program ("applet.class"). A bounding box of 150x150 is established, within which the applet will run when a Web browser executes the HTML code and causes the applet code to be invoked. Using JDK 1.0, the sequence for running this applet is: $ javac applet.java $ appletviewer applet.html Some of the newer Web browsers can also run applets. There are a lot of issues around applets that we will discuss at some point. One big one is security. Applets are intended to be downloaded and run by Web browsers, and so their behavior must be constrained to avoid problems with unruly applets causing local problems like wiping the disk clean. PERFORMANCE One of the things you may notice fairly quickly is that Java programs can run more slowly than their counterparts in other languages. For example, this Java program: // sort numbers class bm1 { static final int N = 6500; public static void main(String args[]) { int i; int j; short t; short vec[] = new short[N]; // seed with descending order for (i = 0; i < N; i++) vec[i] = (short)(N - i); // sort into ascending order for (i = 0; i < N - 1; i++) { for (j = i + 1; j < N; j++) { if (vec[i] > vec[j]) { t = vec[i]; vec[i] = vec[j]; vec[j] = t; } } } } } runs about 35-50 times slower than its C++ equivalent (with both JDK on Windows NT and on the Mac with Roaster): // sort numbers #include const int N = 6500; short vec[N]; main() { int i; int j; short t; // seed with descending order for (i = 0; i < N; i++) vec[i] = N - i; // sort into ascending order for (i = 0; i < N - 1; i++) { for (j = i + 1; j < N; j++) { if (vec[i] > vec[j]) { t = vec[i]; vec[i] = vec[j]; vec[j] = t; } } } return 0; } This is due to the interpreted nature of Java and also due to the extra runtime checking that it does (such as for out of bounds vector subscripts). This particular sorting algorithm isn't very efficient (it has N**2 performance), but is illustrative of the point. Consider another Java program: // number crunching class bm2 { public static void main(String args[]) { long i = 1200000L; double d; while (i > 0) { d = Math.sqrt((double)i) + Math.log((double)i); i--; } } } This program is only about 3 times slower than the equivalent C++: // number crunching #include main() { long i = 1200000L; double d; while (i > 0) { d = sqrt(double(i)) + log(double(i)); i--; } return 0; } Why the difference? Well, in this second program, most of the time is taken in executing the math functions sqrt() and log(). These functions are typically implemented in hardware or via low-level software routines ("native" methods). If most of the time is spent in them (math functions are typically quite expensive), then the interpretive overhead required to handle the loop iterations and so on is less important. In many cases this difference in performance isn't very important. For example, if a Java program or applet is highly interactive or operates across the Internet, then the delays incurred by waiting on humans and networks will be more important than the raw speed of the language. It remains to be seen just how Java performance will shake out. There are several possibilities for improving it. One is to generate native code, but this works against the portability of Java applets and applications. Another similar technique that has been mentioned is a Java to C translator ("java2c"), but this also works against portability. There is a technique called "just in time" compilation that is being considered. The technique involves doing dynamic compilation within the bytecode interpreter, so that the next time a bytecode is executed, the actual machine code for a given platform is substituted in its place. ACKNOWLEDGEMENTS Thanks to Thierry Ciot 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: http://www.rmii.com/~glenm ------------------------- 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 ---------- Issue #002 March, 1996 Contents: Comparing C/C++ With Java Part 2 - Sizes of Primitive Types Chars, Unicode, and File I/O A Way of Doing Class Initialization What Happens When You Output a Character? An Annotated Example of Java Usage Interfacing to an Applet INTRODUCTION In this issue we will continue to introduce Java the language, with the centerpiece of the issue a substantial annotated program example. We will also talk about Java I/O in several contexts, and show an interesting technique for doing class initialization. COMPARING C/C++ WITH JAVA PART 2 - SIZES OF PRIMITIVE TYPES If you've used C or C++ at all you will be familiar with common fundamental types like char, int, and double. Java also has these types, but with a couple of twists. The first new angle is that the types are of uniform size across all Java implementations. Specifically, sizes in bits are: boolean N/A byte 8 char 16 short 16 int 32 long 64 float 32 double 64 The boolean type is not integral and so no size is listed. It can have the values true and false. The character type is 16 bits using the Unicode character set, more about which below. The advantages of uniform sizes are obvious. Even today it is still very easy to stumble across code that is non-portable because someone assumed that an "int" would hold more than 16 bits (it doesn't on most PCs) or that a long and a pointer are the same size. Java has no sizeof() operator like C and C++. With uniform data sizes, and the compiler handling the details of computing the size of space needed for an allocation statement like: long x[] = new long[189]; there is not the same need for such an operator. One drawback to this approach is that if the size of a data type is not a "natural" fit with the underlying hardware, some penalties in performance can be expected. For example, it's true today that the natural size for long is 32 bits on many machines, and requiring that such a type be 64 bits may result in slower code. But with the rapid pace of change in hardware, this concern isn't that significant, especially when weighed against the benefits of uniform sizes. 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()); } } } } 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: Interface to Calculator Applet This HTML is similar to that illustrated in newsletter #001, save for the line: 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 ACKNOWLEDGEMENTS Thanks to Thierry Ciot, 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: http://www.rmii.com/~glenm ------------------------- 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 ---------- Issue #004 April, 1996 Contents: Random Numbers in Java Comparing C/C++ with Java Part 4 - Templates Book Review Java Program Packaging Part 2 - Public/Private Performance - String Operations RANDOM NUMBERS IN JAVA 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); COMPARING C/C++ WITH JAVA PART 4 - TEMPLATES Suppose that you have a vector of numbers or strings or other objects that you'd like to sort. How would you do this? One way is to devise a specialized sorting function unique to your program, that is, a function that simply sorts whatever vector is at hand. This is fine but it's wasteful to have to repeatedly implement the same sorting algorithm for slightly differing situations across a variety of programs. To help solve this problem, C has a standard library function that implements the Quicksort algorithm: void qsort(void* base, size_t n, size_t size, int (*cmp)(const void*, const void*)); The idea is that you pass in a pointer to the base of a data structure, along with the number of elements in the structure and the size of each. You also pass in a function pointer. The function is called with pairs of elements and it returns < 0, 0, or > 0 to specify ordering of the elements. This approach is fairly low-level but works pretty well. C++ also has access to qsort(). In C++ there is also the notion of function templates, a function that can be parameterized with a type parameter so that the same function skeleton can operate on different types. For example, a simple sort might look like: template void sort(T vec[], int n) { for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) { if (vec[i] > vec[j]) { T t = vec[i]; vec[i] = vec[j]; vec[j] = t; } } } } This will work for all numeric types and for any class types for which relational operators, initialization, and assignment are defined. Note that the sorting algorithm in this example has running time proportional to N**2 and we would want to use a more efficient one in production code. Java does not have templates or user-visible pointers or sizeof(), so the above approaches will not work. One possible alternative would be to exploit the idea of all Java class types being derived from a single Object base. This means that one can have a vector of Object references containing actual Objects, Strings, numerics (using wrappers), and so on. A reference to any object of a derived class type can be assigned to an Object reference. For example, I can say: Object vec[] = new Object[10]; for (int i = 0; i < 10; i++) vec[i] = new Integer(i); to represent the numbers 0-9 in a vector. The Vector class in the Java library supports this type of usage. But representing numbers or Strings or other entities in this way doesn't solve the sorting problem. Object is the base of all class types, and it contains an equals() method for comparing two objects for equality, but there is no method for determining the ordering of two objects. For example, given two slots of a Vector, containing references to the objects "Integer(37)" and "Integer(47)", there is no direct way of ordering these. What can be done is to interrogate the actual type of the object and obtain its value indirectly: if (vec[5] instanceof Integer) value = ((Integer)vec[5]).intValue(); "instanceof" is an operator that returns true if the object on its left side is an instance of the class found on the right side. Using a technique like this, it would be possible to write a method that accepted a vector of Object references, and sort that vector by querying and extracting the numbers in it and exchanging their references in the slots of the vector. The Object wrapper approach is quite flexible and general but at the expense of speed and space. Another approach is to special-case common types like int and String, and add an interface with a name like Orderable for ordering arbitrary class type objects. A class that implements Orderable is guaranteed to define a method compareTo() that will order object instances. The types int, String, Orderable, and Object can be supported within a single List class using overloaded methods for adding new elements to the list. Actual elements such as ints can be directly represented using an int[] vector that grows as new elements are added, and operations like binary searching, sorting, and binary trees can be implemented efficiently. This approach is ugly on the inside but presents a fairly clean interface to the user. It is possible to simulate some of the features of C++ templates by using macros such as #define in C/C++, or macro processing tools like the tool "m4" available in UNIX. Java has no macro processing, though one could add auxiliary tools if desired. Are templates very important? This is a hard question to answer. In C++, they are used heavily, for example in STL, the Standard Template Library. In certain situations they solve a messy problem in an elegant way. But C++ templates are also very complex and hard to implement and master, and so it's possible to debate the issue without reaching any definite conclusions. There's something to be said for simpler language features, even if the result is more verbose code. BOOK REVIEW An excellent all-in-one volume on Java is the book "Java in a Nutshell" by David Flanagan (O'Reilly & Associates, $15). It's about 400 pages and covers both the language and libraries, with many examples and a comprehensive index. The book has a description of each library class, such as String or Vector, and presents the interface of each in a handy condensed form. It also covers I/O, graphics, networking, and development tools. The book is handy to keep around for quick reference. Thanks to Herb Weiner for the suggestion. 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". PERFORMANCE - STRING OPERATIONS Java is a young language and it's hard to say for sure just how some of the performance aspects of the language will shake out. But we will offer from time to time some performance tips that may be useful. Many performance techniques apply in any programming language. Suppose that you want to create and print a list of numbers, like so: public class test1 { public static void main(String args[]) { String s = "["; for (int i = 1; i <= 2000; i++) { if (i > 1) s += ","; s += Integer.toString(i, 10); } s += "]"; System.out.println(s); } } This works OK but seems kind of slow. It takes about 13 seconds using JDK 1.0 on a Pentium. When we profile the program, we find that it seems to be doing lots of data shuffling and so forth, with the garbage collector called 40 times. It turns out that using "+=" on Strings is quite expensive, and the reason is that Strings themselves are immutable, that is, are not changed after creation. To append to a String you must copy out the String to a StringBuffer and append to it and then convert it back. StringBuffers are used for doing operations on Strings, like "+" and "+=". This idea can be illustrated by the following example: public class test2 { public static void main(String args[]) { String s = "aaa"; // sequence #1 s += "bbb"; System.out.println(s); String ss = "ccc"; // sequence #2 StringBuffer sb = new StringBuffer(); sb.append(ss); sb.append("ddd"); String ss_save = ss; ss = sb.toString(); System.out.println(ss_save); System.out.println(ss); } } These two sequences are equivalent; using "+=" causes the processing shown in sequence #2 (except for the "ss_save" line). Note that we captured the old value of ss before ss was changed to point to a new String. The old String didn't change when we reassigned ss, we just changed a reference that pointed at it to point at a new String. Going back to the original example, we can rewrite it as: public class test3 { public static void main(String args[]) { StringBuffer sb = new StringBuffer(); sb.append("["); for (int i = 1; i <= 2000; i++) { if (i > 1) sb.append(","); sb.append(Integer.toString(i, 10)); } sb.append("]"); String s = sb.toString(); System.out.println(s); } } resulting in about a 6X speedup. A useful tool for performance tuning is the Java profiler, which can help you find bottlenecks in your code. Using JDK 1.0, one can say: $ javac test.java $ java -prof test resulting in a file "java.prof" being written. Lines of this file contain a count of the number of calls, the name of a called method, the name of the method doing the calling, and a time in milliseconds. Using an Awk script such as that shown below, you can summarize this file into a form similar to prof(1) output for UNIX: 100.0% time = 13.07 seconds 36.0 14895 java/lang/StringBuffer.ensureCapacity(I)V 30.9 18002 java/lang/System.arraycopy(Ljava/lang/Object; ... 15.4 40 java/lang/System.gc()V 3.0 2001 java/lang/Integer.toString(II)Ljava/lang/String; 2.8 8002 java/lang/StringBuffer.append(Ljava/lang/String ... 2.0 1 t2.main([Ljava/lang/String;)V 1.5 4001 java/lang/String.(Ljava/lang/StringBuffer;)V 1.3 6893 java/lang/StringBuffer.append(C)Ljava/lang ... 1.3 6002 java/lang/StringBuffer.(I)V I was told by someone at Sun that the format of the java.prof file will change in a future JDK release, so be careful when using this script with such releases. #!/bin/sh awk ' $1 == "#" && $2 == "count" { flag = 1; next; } $1 == "#" && $2 != "count" { flag = 0; next; } { if (flag) { n++; data[n,1] = $1; data[n,2] = $2; data[n,3] = $3; data[n,4] = $4; } } END { for (i = 1; i <= n; i++) { cnt[data[i,2]] += data[i,1]; tim[data[i,2]] += data[i,4]; } for (i in tim) { for (j = 1; j <= n; j++) { if (i == data[j,3]) tim[i] -= data[j,4]; } total += tim[i]; } printf "%5.1f%% time = %.2f seconds\n", 100.0, total / 1000.0; for (i in cnt) { printf "%5.1f %7ld %s\n", tim[i] * 100.0 / total, cnt[i], i; } } ' 0 if p1 > p2 public int compareTo(Object p1, Object p2); } This looks somewhat like a class, except that all the methods are abstract, that is, are not implemented immediately but serve as placeholders. What is an interface used for? Suppose that you want to ensure that a particular class has certain properties, in the present case that it defines the method compareTo() to order objects. You can enforce this by requiring that the class implement interface Orderable: public class xxx implements Orderable { public int compareTo(Object p1, Object p2) { int n1 = ((Integer)p1).intValue(); int n2 = ((Integer)p2).intValue(); return n1 - n2; } } Why do we care about this? In the next section we will talk about sorting in Java. To write a semi-general sort method, it's necessary to assume that the objects within a Vector of objects to be sorted have some ordering method available to rank them. Java by default has no way of ordering two arbitrary objects. By defining an Orderable interface, and either sorting a vector of Orderables (that is, a vector of object types where the type is defined to implement Orderable), or sorting a Vector of Objects with a compareTo() method passed to the sort routine via a method wrapper (see next section), we can ensure that a compareTo() method is available. An interface is sort of like a base class for a class. If you have an Orderable reference: Orderable or = null; it's possible to assign to it a reference to an object type that implements Orderable: xxx x = new xxx(); or = x; Again, if we have an Orderable object reference, we can be sure that a method call like: or.compareTo(p1, p2) will be valid. SORTING IN JAVA 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. JAVA PROGRAM PACKAGING PART 3 - 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. PERFORMANCE - METHOD CALL OVERHEAD In a language such as C or C++ you may have encountered the idea that calling a function ("method" in Java) has some time overhead associated with it beyond the actual processing done by the function. For example, with this code: int max(int a, int b) { return (a > b ? a : b); } int main() { int i = 0; int j = 0; int k = 0; long n = 50000000L; while (n-- > 0) //k = max(i, j); k = (i > j ? i : j); return 0; } coding the actual calculation of the maximum of two ints is about three times as fast when done inline as when done via a call to max(). In C++ there is an "inline" specifier that can be used to specify that a function should be expanded inline if possible. Function call overhead is caused by various factors, including time spent in setting up stack frames, actual transfer of control to the called function, and so on. Java also has overhead with calling methods. For example, in this code: public class xx { public /*final*/ int max(int a, int b) { return (a > b ? a : b); } public static void main(String args[]) { int i = 0; int j = 0; int k = 0; int n = 1000000; xx p = new xx(); while (n-- > 0) //k = p.max(i, j); k = (i > j ? i : j); } } doing the max computation inline is about twice as fast as calling the method max(). In Java one reason for overhead is that methods are virtual by default, that is, the actual method that is called is determined at run time by considering the actual object type. For example, the Object class, from which all other class types derive, has a method hashCode() defined in it. A class that extends from Object may also have a hashCode() method. If in a program you have an Object reference, and wish to call hashCode(): public void f(Object p) { int h = p.hashCode(); } then the actual version of hashCode() that is called depends on whether p is "really" an Object or refers to an instance of a class derived from Object. So, because a method is virtual by default, it is difficult or impossible to expand it as inline (in C++ there have been some clever optimizations that have been used to make virtual functions inline, but this is a hard problem). The keyword "final" can be used to say "this method cannot be overridden" by another method in a derived class. With JDK 1.0 this optimization has no effect, but over the long term specifying "final" is likely to be an important optimization. Note that "final" has various other meanings, for example saying: final int N = 10; marks N as unchangeable, and a final class cannot be extended from. Should you worry about method call overhead? Most of the time, no. It's only when you have a method that's called very heavily in a loop that this overhead matters. 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: http://www.rmii.com/~glenm 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 ---------- Issue #006 June, 1996 Contents: JavaDoc Comparing C/C++ to Java Part 6 - Method Overloading Introduction to Applet Programming Part 2 - Drawing Performance - Primitive Types vs. Wrappers 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. COMPARING C/C++ TO JAVA PART 6 - METHOD OVERLOADING In both C++ and Java it's possible to have functions/methods with the same name as other functions. For example, in C++ you can say: class A { public: void f(int i) {} void f(char* s) {} }; and the compiler will choose the right function based on the type of the argument given to f(). Java has a similar facility. One question that naturally arises is how to choose the proper function or method to call. For example, with this C++ code: void f(short s) {} void f(long l) {} and a call: char c = 0; f(c); it's not clear which of these functions to call, because a char could be promoted to either a short or a long (this particular call is in fact ambiguous in C++). Java uses what is known as a "most specific" algorithm to figure out which method to call. Before we explain what is meant by this, we need to talk a bit about assignment, namely, what sorts of types can be assigned to other types in Java. Here is an example to illustrate this point: public class test1 { public static void main(String args[]) { short s = 0; int i = 0; float f = 0.0f; i = s; // OK f = i; s = i; // error i = f; s = (short)i; // OK but have to be careful i = (int)f; } } We can assign shorter primitive types to longer ones, for example short (16 bits) to int (32 bits). We can assign an int to a float. We cannot assign a longer type to a shorter or a float to an int, unless we use a cast. With casting one has to be careful, because of potential loss of precision, for example, in stuffing 32 bits into 16. Similarly, we can assign class object references to references to a base class, but not the other way around without casting. Given this notion of assignment, and an actual method call, how does Java decide which method to call? First of all, it finds all methods that have the correct name, the correct number of parameters, and parameters that are of types that can be assigned the values of the arguments. If only one method matches, then that is the method that is invoked. If there's more than one method in the set under consideration, then the compiler goes through and finds methods for which all the parameter types are assignable to another method's parameter types in the set, and removes that other method from the set of methods being considered. The method removed is considered "less specific". If the result of this process is a single method, then that method is invoked. If there's more than one method left, the call is ambiguous. To illustrate these ideas, consider another example: public class test2 { public static void f1(int i, int j) {} public static void f1(int i, short j) {} public static void f2(long i, long j) {} public static void f2(int i, int j) {} public static void f3(short i, short j) {} public static void f4(double i, float j) {} public static void f4(float i, double j) {} public static void main(String args[]) { int i = 0; short s = 0; f1(i, s); f2(i, s); f3(i, s); f4(i, s); } } The f1() call invokes f1(int, short) and is an exact match. The f2() call invokes f2(int, int) because f2(int, int) has parameter types that are assignable to f2(long, long) and f2(long, long) is therefore removed from the set. The f3() call is an error because an int for the first parameter cannot be assigned to a short. The f4() call is ambiguous because both methods can be called and neither can be removed from the set of possible methods to call because neither has parameter types assignable to the other. Coming back to C++ for a moment, it's fair to say that the equivalent argument matching rules in that language are significantly more complicated, reflecting a more complex language in general. For example, consider a case like: class String { public: String(char); }; where a constructor is defined to create a String from a character. In C++, usage like: String s(37); is valid, with the integer constant 37 being demoted to a char and then the constructor invoked. Whether a user intended this or not is not clear, given that usage such as: String s(12345); is a problem when chars are 8 bits. There's no magic formula for avoiding problems with method overloading, except perhaps to follow the general rule of avoiding cleverness. If a reader of your code can't quickly tell which method is invoked in a given case, then it might be worth reworking the code or at least adding a comment about its operation. INTRODUCTION TO APPLET PROGRAMMING PART 2 - DRAWING In previous issues we gave a simple example of an applet, and talked about the handshaking that goes on between an applet and the applet viewer or Web browser. As you recall, an applet is not a standalone Java program of the sort we've presented examples of here, but instead is a program that runs in a particular context, invoked via a Web browser or applet viewer program. We will continue our discussion by talking about drawing in an applet, things like lines and boxes and colors and fonts. To illustrate these ideas, let's look at an actual applet: import java.applet.*; import java.awt.*; public class test1 extends Applet { public void paint(Graphics g) { g.setColor(Color.red); g.fillOval(20, 20, 350, 200); g.setColor(Color.green); g.setFont(new Font("Helvetica", Font.ITALIC, 36)); g.drawString("This is a test", 80, 120); //g.setColor(Color.blue); g.setColor(new Color(0, 0, 223)); g.drawRect(20, 20, 350, 200); } } that is invoked via a browser or applet viewer using the HTML code: The parameters passed in to the applet set the bounding box (400 x 400 in this example) within which the applet will function. As we've discussed previously, an applet has no main() method, and the paint() method is part of the handshaking protocol for an applet. paint() is called when the applet needs to be drawn. The first thing that paint() does is to set a color for drawing. A color like "red" is a static data member of the Color class found in the Abstract Windowing Toolkit (AWT) in Java. It's also possible to specify your own color, as we illustrate later in the example: g.setColor(new Color(0, 0, 223)); where (0, 0, 223) are red/green/blue values 0-255. This particular color is a dark shade of blue. After setting the color, we draw a filled oval, starting at position (20, 20) relative to the bounding box we've established. The oval has a width of 350 and a height of 200. We now have a red oval on the screen, and we change the drawing color to green and draw some text. In this example, the Helvetica font is used and the text is drawn in italics using 36-point letters. The string originates at (80, 120) in the bounding box. Finally, we draw a box around the oval, using the color we've created from RGB values. The Graphics class in java.awt has a variety of methods for drawing lines, rectangles, ovals, polygons, and strings, with or without filling. There are also various methods for getting and setting colors and fonts. The Color class has a set of standard colors defined, and methods for creating your own colors or querying properties of existing colors. The Font class has similar methods for fonts, and there is also a FontMetrics class for determining the metrics of a specified font, such as might be used to compute the width of a string displayed in a given font. PERFORMANCE - PRIMITIVE TYPES VS. WRAPPERS In Java all class types are derived from the base class Object. Primitive types such as int or double are not class types and do not have this property. But they do have class type wrappers, that can be used to represent the primitive type and hence plug in to the Object hierarchy. For example, the wrapper for int is Integer: Integer x = new Integer(37); Object p = x; In this example we've taken an integer constant (37) and created a class object instance to represent this value. The object instance can be assigned to an Object reference and manipulated in various ways. Wrappers have some overhead associated with them. They offer generality at the cost of inconvenience in digging out the value: int i = ((Integer)p).intValue(); and there are some space costs as well. To more closely examine this latter point, we can write a program that allocates a vector of ints or int wrappers, and compute the size of each vector element: public class test1 { public static final int N = 25000; public static long freemem() { System.gc(); return Runtime.getRuntime().freeMemory(); } // ints without wrappers public static void test01() { long start_mem = freemem(); int vec[] = new int[N]; for (int i = 0; i < N; i++) vec[i] = i; long end_mem = freemem(); long n = (start_mem - end_mem) / N; System.out.println("bytes per element = " + n); } // ints with wrappers public static void test02() { long start_mem = freemem(); Integer vec[] = new Integer[N]; for (int i = 0; i < N; i++) vec[i] = new Integer(i); long end_mem = freemem(); long n = (start_mem - end_mem) / N; System.out.println("bytes per element = " + n); } // driver public static void main(String args[]) { test01(); test02(); } } This program uses a system method freeMemory() that returns the amount of memory currently free. We force a garbage collection via System.gc() to make the figure more reliable. As we mentioned in issue #004, this technique for determining memory use per element should be used cautiously. When we run this program, it reports 4 bytes used per element without wrappers, and 16 per element with a wrapper. A wrapped double reports as 24 bytes per element, with the actual double value as 64 bits (8 bytes). The space overhead of wrappers goes to support things like garbage collection. Wrappers have considerable advantages in that primitive types can be treated in a way similar to class types. See for example the discussion on page 243 of Arnold & Gosling's book "The Java Programming Language" (Addison-Wesley). But they do take extra space and time, which may be an issue in some circumstances. 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: http://www.rmii.com/~glenm 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 ---------- Issue #007 July, 1996 Contents: Invoking Subprograms in Java Comparing C/C++ to Java Part 7 - Destructors vs. Finalize Free Java Library Enums and Java Introduction to Applet Programming Part 3 - Events INVOKING SUBPROGRAMS IN JAVA 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. COMPARING C/C++ TO JAVA PART 7 - DESTRUCTORS VS. FINALIZE In C++ there is the notion of a destructor, a function called when a class object instance goes out of scope. For example: class A { public: A(); ~A(); }; void f() { A a; } When the function f() is entered, the constructor A::A() is called for the local object a, and when f() is exited, the destructor A::~A() is called in turn. Java class objects do not behave in a similar way. They are dynamically allocated, at which point a constructor is run on the object instance, and later garbage collected. Java does not have any notion of a destructor method that is run when an object instance is no longer valid. But it does have a finalize() method: protected void finalize() {} that can be added to any class. finalize() is called for an object instance after that instance has been identified by the garbage collector as no longer a valid object, that is, there are no remaining references to it. For example: public class test1 { public test1() { System.out.println("call ctor"); } protected void finalize() { System.err.println("call finalize"); } public static void f() { test1 p = new test1(); } public static void main(String args[]) { f(); System.gc(); System.runFinalization(); } } After the return from the f() call, there is an object instance that can be garbage collected; there is no reference to the object we created while inside of f(). System.gc() frees up objects and marks them as pending for finalization to be done. Actual finalization processing proceeds asynchronously with the garbage collector, so to force finalization we can call System.runFinalization(). Because of the way that finalize() methods work, you should use caution in assuming that they can be used to reclaim valuable resources such as UNIX file descriptors. Also, you should insert a line like: super.finalize(); at the end of a finalize() method, to call the corresponding method of the superclass. FREE JAVA LIBRARY I have developed a Java library for managing collections of objects, such as lists, stacks, sets, bitmaps, and trees. If you are interested in this library, please see the Web page: http://rmi.net/~glenm/javalib/index.html ENUMS AND JAVA 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. INTRODUCTION TO APPLET PROGRAMMING PART 3 - EVENTS In previous issues we've talked about the basic protocols underlying an applet and how graphical and text output is done. With this issue we'll start a discussion of input handling, and begin by talking about events just a little bit. Here is an applet that allows a user to do free drawing with the mouse. That is, whenever the mouse button is held down and the mouse moved, a line will be drawn: import java.applet.*; import java.awt.*; public class Draw extends Applet { int prev_x = 0; int prev_y = 0; int color = 0; 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(); switch (color) { case 0: g.setColor(Color.red); break; case 1: g.setColor(Color.green); break; case 2: g.setColor(Color.blue); break; } if (++color > 2) color = 0; g.drawLine(prev_x, prev_y, x, y); prev_x = x; prev_y = y; return true; } } This applet can be invoked via the HTML code: Interface to Draw Applet using the JDK AppletViewer program. We've alternated the output color of the drawn line, as a means of tying together some of the ideas from the last issue, and to better illustrate what is happening. mouseDown() is an applet method called by the system when the user presses a mouse button. It's passed a description of the event along with an X,Y pair, and returns true if it handled the event, so that the event will not be further processed. In our example we simply record the X,Y location where the event occurred. mouseDrag() is a method called when the user drags the mouse with the button down. When this occurs, we switch colors and then draw a line between the old position and the new one. getGraphics() is a method that returns a java.awt.Graphics object, that is used for line drawing, image painting, and so on. If you run the above applet, the colors will alternate very rapidly if you move the mouse slowly, and alternate more slowly if you move the mouse rapidly. Why is this? It has to do with polling of the mouse. That is, the mouse's status is checked at regular intervals. If the mouse has moved a long ways in the poll interval, then the single-color line segment will be longer than if the mouse had been moved slowly. We will be discussing events and input further in upcoming issues. 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: http://www.rmii.com/~glenm 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 ---------- Issue #008 August, 1996 Contents: Managing Directories in Java Comparing C/C++ With Java Part 8 - Typedefs Introduction to Applet Programming Part 4 - Button Input Performance - charAt() MANAGING DIRECTORIES IN JAVA 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. COMPARING C/C++ WITH JAVA PART 8 - TYPEDEFS In C and C++, there is a facility known as typedefs, whereby you can define a name to represent a type. For example: typedef unsigned char* T; says that T is a type of pointer to unsigned character. Once a typedef has been specified, it can be used wherever a type is required, for example to declare a variable: void f() { T x; } Java has no typedefs, and it's interesting to consider this point. There are at least three reasons why typedefs are useful in C and C++. The first reason is to define types as an aid to portability. For example, a typedef may be defined to represent a 32-bit integer: typedef int INT32; // on workstation typedef long INT32; // on PC Integral types are of varying sizes in C and C++. This is not an issue in Java, where types are of uniform sizes. For example, an int is 32 bits, and a long 64. Another reason for typedefs in C is to introduce an alias for a structure name: struct A { int x; }; typedef struct A A; A x; In C++ and Java this usage is implicit in the language. Finally, typedefs are useful to represent complicated type combinations, as in: typedef unsigned long** T; which says that T is a pointer to a pointer to an unsigned long integer. Java has no-user visible pointers, nor does it have unsigned modifiers. So a typedef here doesn't buy very much. Typedefs are still useful simply for abstraction purposes, that is, as an abstract way of naming a type, for example "Age" instead of "int". But much of their usage in C and C++ doesn't apply in Java, and it's a reasonable decision to leave typedefs out of the language. INTRODUCTION TO APPLET PROGRAMMING PART 4 - 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: Interface to Text Applet 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. PERFORMANCE - CHARAT() Suppose that you have a need to find a character in a string, that is, return a position >= 0 of where a character occurs, or -1 if not found. In a language like C, with its pointers and low-level approach to programming, it's often just as efficient or more so to code a loop for finding a character directly, instead of using a library function like strchr(). Especially with short strings, avoiding the function call overhead is often worth a lot. Is this true of Java? Suppose that we code up a similar example: public class index { public static void main(String args[]) { String s = "aaaaaaaaaa"; int i = 250000; int n = 0; // method #1 if (args[0].compareTo("index") == 0) { while (i-- > 0) n = s.indexOf('x'); } // method #2 else { while (i-- > 0) { int len = s.length(); n = -1; for (int j = 0; j < len; j++) { if (s.charAt(j) == 'x') { n = j; break; } } } } } } When we run this code with JDK 1.0, we find that method #1 is about 3X as fast as method #2, which is the equivalent of using pointers in C. Why is this? There are a couple of reasons. One is that charAt() is a method call, and not merely a quick peek that retrieves a character at a given position. The other reason for the slower performance is that charAt() checks the index to ensure that it's within bounds. So, even though we're iterating over the characters of a String in a safe way (0 to s.length()-1), the checks are done anyway. Because of the method call overhead and the index checking, indexOf() wins easily. It avoids the method call overhead, and the index checking is not done but instead characters are accessed directly from the internal char[] vector that underlies a String. It's probably a little early to say how an area like this one will shake out. charAt() could conceivably be expanded as an inline, though it's not declared as a final method in JDK 1.0. And the subscript checking is likely to be left in place, because it's part of what Java guarantees to the programmer. 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: http://www.rmii.com/~glenm 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 ---------- Issue #009 September, 1996 Contents: Comparing C/C++ and Java Part 9 - Operators Introduction to Applet Programming Part 5 - Text Input Performance - Fast I/O For Text Lines COMPARING C/C++ AND JAVA PART 9 - OPERATORS If you know C or C++, many of the operators found in Java will be familiar. But there are also some differences worth noting. 1. Java has no unsigned data types, and so the operator >>> has been added to support unsigned right shifting. That is, a right shift using >> will propagate the sign, whereas a shift using >>> will zero fill the high bits. >>>= also has been added. The operator "instanceof" can be used to determine whether an object is an instance of a given class, as in: if (s instanceof String) // do something 2. Java does not have any of the following C/C++ operators: , -> .* ->* :: 3. In Java "*" and "&" have no meaning for doing pointer arithmetic or addressing, because Java has no user-visible pointers. 4. Java has no operator overloading, for example overloading [] to provide a "smart" vector type of some sort. 5. In Java "+" can be used for String concatenation. 6. Finally, as a quick summary of Java operators, here is a table of operators and their precedences, highest to lowest. This table is taken from Arnold & Gosling's "The Java Programming Language", page 300: postfix [] . (params) expr++ expr-- unary ++expr --expr +expr -expr ~ ! creation/cast new (type)expr multiplicative * / % additive + - shift << >> >>> relational < > >= <= instanceof equality == != bitwise AND & bitwise exclusive OR ^ bitwise inclusive OR | logical AND && logical OR || conditional ?: assignment = += -= *= /= %= >>= <<= >>>= &= ^= |= INTRODUCTION TO APPLET PROGRAMMING PART 5 - 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: Interface to Text Applet 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. PERFORMANCE - 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 *"); } } } 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: http://www.rmii.com/~glenm 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 ---------- Issue #010 October, 1996 Contents: The Finally Clause and Method Exiting Comparing C/C++ and Java Part 10 - Labelled Break and Continue Performance - Garbage Collection and Setting to Null Newsletter Writing Introduction to Applet Programming Part 6 - Text Fields REVIEWERS NEEDED I am looking for a couple of additional people to help review this newsletter. If you are a reviewer, you receive the issue a few days early, and send me your comments. The present set of reviewers have done much to improve the quality of the newsletter. If you have Java experience and are interested in this, please send me a note (glenm@glenmccl.com). 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. COMPARING C/C++ AND JAVA PART 10 - LABELLED BREAK AND CONTINUE In C and C++, the break and continue statements are used to break out of and continue with the next iteration of loops, for example while or for loops. break is also used in switch statements. Such breaking and continuing apply only to the enclosing loop. Sometimes you'd like more flexibility, for example the ability to break out two levels. In C, you might say: for (i = 1; i <= 10; i++) { for (j = 1; j <= 10; j++) { if (some condition) goto done; /* other processing in loop */ } } done: /* other processing */ This effect could also be achieved by breaking out of the inner loop, and then testing some program state variable, and then breaking out of the outer loop. In Java, there's a simpler approach to solving this problem. The above could be written as: outer_block: for (i = 1; i <= 10; i++) { for (j = 1; j <= 10; j++) { if (some condition) break outer_block; // other processing in loop } } // control comes here when break is executed The break statement has a label. When the statement is executed, control transfers out of the enclosed labelled statement. Control is NOT transferred to the label, but to a point past the end of the labelled statement. A labelled continue statement works in a similar way; control is transferred to the end of a labelled loop. Note that with both break and continue, any finally block will be executed before the labelled statement is exited or the next iteration of a loop takes place. For example: loop: while (some condition) { try { if (some condition) continue loop; // other processing } finally { // some processing } } The labelled continue statement will cause the finally block to be executed before going to the bottom of the while loop in preparation for the next iteration of the loop. PERFORMANCE - 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. NEWSLETTER WRITING Would your company find it valuable to have a customized newsletter on C++ or Java or related topics, similar to this newsletter that you're reading right now? If so, please contact me (glenm@glenmccl.com) for further information. Custom newsletters can consist of technical material furnished from outside, internal material with outside editting, or a combination of the two. INTRODUCTION TO APPLET PROGRAMMING PART 6 - 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: Interface to Text Applet Please enter your name and address then select Done.

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. ACKNOWLEDGEMENTS Thanks to Thierry Ciot, Irv Kanode, and Mike Paluka 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: rmi.net /pub2/glenm/javalett or on the Web at: http://rmi.net/~glenm 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: rmi.net /pub2/glenm/javalett (for back issues) Web: http://rmi.net/~glenm ---------- Issue #011 November, 1996 Contents: Newsletter Index Java Language Specification Comparing C/C++ and Java Part 11 - Preprocessing Introduction to Applet Programming Part 7 - Animation Performance - Binary Search Class NEWSLETTER INDEX An index to all the Java and C++ newsletters can be found on the Web at: http://rainbow.rmi.net/~glenm The index will be updated as new issues come out. JAVA LANGUAGE SPECIFICATION This is the name of a recently-published book by Gosling, Joy, and Steele, published by Addison-Wesley. It's about 800 pages and costs around $35. There are about 450 pages on the language proper, and 300 on the core packages like java.lang, java.util, and java.io. Included is a LALR(1) grammar for the language. This is a good book if you're interested in the exact details of how the language and associated packages work. It's not a tutorial, and does go into a lot of depth about particular language features. COMPARING C/C++ AND JAVA PART 11 - PREPROCESSING C and C++ have a preprocessor that is run over source files before other processing. Java does not have a preprocessor. One use of the preprocessor is to define constants: #define N 79 In Java this can be accomplished by saying: public static final int N = 79; where "public" means that the constant is available to all, "static" means that it's shared by all object instances of a given class, and "final" means it cannot be changed after initialization. Another use of the preprocessor is to define macros: #define max(x, y) ((x) > (y) ? (x) : (y)) This is typically done for speed or readability. A good compiler reduces the need for this facility, by inline expanding short methods. This is an area of Java still in a state of flux, however. The preprocessor can be used to #include other files into a source file. Java packages and naming conventions and import directives (see newsletters #003, #004, and #005) mostly eliminate the need for #include. Finally, the preprocessor has directives like: #ifdef XXX stuff ... #endif to perform conditional compilation, typically on a per-platform basis. Java is a platform-independent language, and in theory such a directive is not needed. In practice, however, it would be useful to be able to do conditional compilation. For example, the AWT (Abstract Windowing Toolkit), may perform differently across platforms, due to the vagaries of the underlying window systems. There's nothing to stop you from using your own preprocessor or macro processor, but such an approach is not part of the official Java language. INTRODUCTION TO APPLET PROGRAMMING PART 7 - 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) { } } } } along with the HTML code that drives the applet: Interface to Animation Applet 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. PERFORMANCE - 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://rainbow.rmi.net/~glenm/javalib/index.html ACKNOWLEDGEMENTS Thanks to Jay Burgess, Thierry Ciot, Irv Kanode, Mike McCann, Mike Paluka, Srihari Sampathkumar, and Bob Shore 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: rmi.net /pub2/glenm/javalett or on the Web at: http://rainbow.rmi.net/~glenm 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: rmi.net /pub2/glenm/javalett (for back issues) Web: http://rainbow.rmi.net/~glenm ---------- Issue #012 December, 1996 Contents: Jack Comparing C/C++ and Java Part 12 - Null JavaScript Introduction to Applet Programming Part 8 - Client/Server Example Correction 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://rmi.net/pub2/glenm/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). COMPARING C/C++ AND JAVA PART 12 - NULL In C and C++, "NULL" is a constant defined in a header file, with a value like: 0 or: 0L or: ((void*)0) depending on the compiler and memory model options. NULL is not, strictly speaking, part of C/C++ itself. In Java, "null" is not a keyword, but a special literal of the null type. It can be cast to any reference type, but not to any primitive type such as int or boolean. The null literal doesn't necessarily have value zero. And it is impossible to cast to the null type or declare a variable of this type. 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 and . 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: Link #1
Link #2
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 "
" 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. INTRODUCTION TO APPLET PROGRAMMING PART 8 - 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: Client Applet 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. CORRECTION In issue #011 we presented an example of animation. Steve Drach pointed out that a call to dispose() is needed, as follows: // 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; // other switch cases ... } // sleep for a second Thread.sleep(1000); } catch (InterruptedException e) { } g.dispose(); // <<<<<<<<<<<<<<<<<<<< } } Without this, the animation will eventually stall out. This is different from garbage collection, which is automatic, and involves freeing of windows resources. That is, getGraphics() retrieves one of a limited number of system window resources, which must be returned via dispose(). A similar case might come up when dealing with a resource such as UNIX file descriptors. ACKNOWLEDGEMENTS Thanks to Jay Burgess, Thierry Ciot, Irv Kanode, Mike McCann, Mike Paluka, Srihari Sampathkumar, and Bob Shore 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: rmi.net /pub2/glenm/javalett or on the Web at: http://rainbow.rmi.net/~glenm 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: rmi.net /pub2/glenm/javalett (for back issues) Web: http://rainbow.rmi.net/~glenm ---------- Issue #013 January, 1997 Contents: JDK 1.1 Reflection 1.1 Performance Indexing Program Comparing C/C++ and Java Part 13 - Arrays Conditional Compilation Introduction to Applet Programming Part 9 - Image Loading and Filtering JDK 1.1 A major upgrade to Java has been released by Sun in beta form, Java Development Kit 1.1. It's available via FTP from Sun at: http://java.sun.com/products/JDK/1.1 We will be using JDK 1.1 in subsequent newsletter discussions and examples. There are many new features in 1.1, including Remote Method Invocation, SQL support, security features, text processing, reflection, and so on. 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. 1.1 PERFORMANCE In issue #001, we presented a crude benchmark for assessing run time performance: // sort numbers public class bm1 { static final int N = 6500; public static void main(String args[]) { int i; int j; short t; short vec[] = new short[N]; // seed with descending order for (i = 0; i < N; i++) vec[i] = (short)(N - i); // sort into ascending order for (i = 0; i < N - 1; i++) { for (j = i + 1; j < N; j++) { if (vec[i] > vec[j]) { t = vec[i]; vec[i] = vec[j]; vec[j] = t; } } } } } With JDK 1.0 and Borland C++ 5.0, running on Windows NT 3.51, and compiled with optimization on, the Java version of this program runs about 43 times slower than the C++ version. With JDK 1.1 beta, as mentioned above, the difference is about 17 times, or a speedup of around 2.5 for the Java interpreter. This is presumably due to rewriting the main interpreter loop in assembly language. There are further speedups possible, such as use of Just In Time compilation technology. And this benchmark is worst-case in some ways, in that it does no I/O, network access, or number crunching using functions like log() and sin(). For example, low-level Java I/O appears to be around 3X slower than its C counterpart, and a program heavily dependent on I/O will not have the performance difference observed with this sorting example (although higher-level Java I/O has some significant differences from its C counterpart as far as performance goes). Also, the Java language is aimed at a slightly different sort of application than C, and comparisons of this sort are never perfect. INDEXING PROGRAM As an example of using Java to write conventional applications, as compared to applets or networking, I have written a Java program that will go through all the files in a specified directory structure and extract words in them and insert these into an index. You can then later specify words and be told which files contain those words. This is something like what Web search engines do. The program indexes at around 10 MB/minute on a slow Pentium, and lookup is nearly instantaneous. The indexes are about 3-5% of the size of the input files across a mixture of file types. If you're interested in trying an early version of this program, please let me know. You need Java, preferably a faster version like JDK 1.1. COMPARING C/C++ AND JAVA PART 13 - ARRAYS Java has arrays that in actual use look like conventional arrays in C or C++. For example, if you want to sum the first ten elements of an array, you might say: int sum = 0; for (int i = 0; i < 10; i++) sum += a[i]; But there are some major differences in Java's treatment of arrays. One big one is that Java checks array subscripts, and if instead of the above I'd said: int sum = 0; for (int i = 0; i < 11; i++) sum += a[i]; an exception would be thrown, assuming that "a" is in fact an array of ten elements. Another difference is that a Java array may be of zero length, for example: int x[] = new int[0]; C++ supports this but C does not. The length of the array is available at any time by saying: int len = x.length; ".length" is not a function call or anything, just a specifier that allows the length to be retrieved at will. This is the actual length of the array. If, for example, you have an array of integers, of length 20, and you've only inserted values into the first ten slots, then there's no general way of knowing that except by a convention you yourself adopt (such as ending the list with -1). Array names are references, and thus it's possible to "assign" to an array like so: int x[] = new int[10]; int y[] = null; y = x; In C/C++, actual arrays may not be assigned to: int x[10]; int y[10]; y = x; // invalid though pointers representing arrays certainly may be. In C++, code like: class A { public: A(); }; A* p = new A[10]; will result in ten constructor calls, one for each array slot, whereas in Java, code of the form: public class A { A() {} A p[] = new A[10]; } will not. Rather, an array of ten references to A class instances will be created, each initialized to the null value. In other words, the new call in C++ will allocate the space and then arrange for A's constructor to be called on each array slot, whereas with Java, you must initialize the array by saying: for (int i = 0; i < 10; i++) p[i] = new A(); Finally, it's possible to assign array objects to Object references, as in: Object p = null; int x[] = new int[10]; p = x; and manipulate the array through an Object reference. An array type is a special type of class, and an actual instance of an array is considered to be a class object. Whether all of these properties of Java arrays are "good", for example, the forced subscript checking, is of course a matter of personal philosophy. There are tradeoffs to be made in getting down to the bare metal, like C does, vs. a higher-level approach where the programming system is doing more for you. 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. INTRODUCTION TO APPLET PROGRAMMING PART 9 - 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; int g = ((rgb & 0xff00) + 0xff00) / 2; int b = ((rgb & 0xff) + 0xff) / 2; //return a | r | g | b; return a | 0 | 0 | 0; } } which is driven by HTML code: Interface to Filter Applet "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. ACKNOWLEDGEMENTS Thanks to Jay Burgess, Thierry Ciot, Irv Kanode, Mike McCann, Mike Paluka, Srihari Sampathkumar, and Bob Shore 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: rmi.net /pub2/glenm/javalett or on the Web at: http://rainbow.rmi.net/~glenm 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) 1997 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: rmi.net /pub2/glenm/javalett (for back issues) Web: http://rainbow.rmi.net/~glenm ---------- Issue #014 February, 1997 Contents: Serialization Introduction to Java Security Part 1 - Virtual Machine Web Site for Java Indexing Software Comparing C/C++ and Java Part 14 - Local Declarations Introduction to AWT Programming Part 1 - A Simple Example Correction 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. INTRODUCTION TO JAVA SECURITY PART 1 - 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. WEB SITE FOR JAVA INDEXING SOFTWARE We mentioned last time about the availability of a trial version of some Java software for indexing files on disk. If you want to find out further about this, you can look at the Web site: http://www.sni.net/~glenm/index/index.htm COMPARING C/C++ AND JAVA PART 14 - LOCAL DECLARATIONS In C, when you declare local variables, it's necessary to group them all together at the top of a function: void f() { int i; double d; char* p; // stuff } C++ and Java relax this restriction, and allow the declaration of variables anywhere within the function or method: void f() { int i = 0; g(i); String s = "xxx"; h(s); } and so on. The scope of such declarations is to the end of the block they're declared in. It's also possible to declare loop variables within the loop initialization, as in: for (int i = 1; i <= 10; i++) { System.out.println(i); } i = 37; // invalid and the scope is to the end of the for block. This feature is useful in several ways. In general, it's best to minimize the scope of a given declaration, to help in avoiding dumb programming errors (for example in "recycling" a variable), and doing so can also help to optimize memory management. The tradeoff is that introducing declarations in the middle of a block of code can be confusing to the reader of that code. INTRODUCTION TO AWT PROGRAMMING PART 1 - A SIMPLE EXAMPLE 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. CORRECTION In issue #013 there was this bit of code, dealing with image filtering: int a = rgb & 0xff000000; int r = ((rgb & 0xff0000) + 0xff0000) / 2; int g = ((rgb & 0xff00) + 0xff00) / 2; int b = ((rgb & 0xff) + 0xff) / 2; This should be: 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; Without this change, a low bit can "leak" into the next 8-bit slot in the 32-bit quantity. Thanks to David Springer for pointing this out. ACKNOWLEDGEMENTS Thanks to Jay Burgess, Thierry Ciot, Irv Kanode, Mike Paluka, Srihari Sampathkumar, Jason Sharp, and Bob Shore 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: rmi.net /pub2/glenm/javalett or on the Web at: http://rainbow.rmi.net/~glenm 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) 1997 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: rmi.net /pub2/glenm/javalett (for back issues) Web: http://rainbow.rmi.net/~glenm ---------- Issue #015 March, 1997 Contents: Java Test Suites JDK 1.1 Introduction to Java Security Part 2 - Verification Books on Java Comparing C/C++ and Java Part 15 - Bitfields Introduction to AWT Programming Part 2 - Another Example JAVA TEST SUITES I am currently working on developing commercial test suites for several of the Java API packages (libraries). The first two of these will cover reflection (java.lang.reflect) and utilities (java.util). These suites exhaustively test the features of the API. For example, the one on reflection is about 6000 lines of code. If you have an interest in obtaining these products, please contact me (glenm@glenmccl.com). JDK 1.1 Javasoft has released the official version of the Java Development Kit 1.1. You can download it at: http://java.sun.com/products/JDK/1.1 JDK 1.1 contains many new features, such as reflection and serialization. INTRODUCTION TO JAVA SECURITY PART 2 - 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: - stack overflows - no illegal data conversions - access to public/private/protected - proper format for the .class file - methods have appropriate arguments 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. BOOKS ON JAVA If you have Web access, there is a page that has a comprehensive list of Java books that have been published or are in the works: http://lightyear.ncsa.uiuc.edu/~srp/java/javabooks.html Several hundred books are listed. COMPARING C/C++ AND JAVA PART 15 - BITFIELDS In C and C++, it's possible to have usage such as: struct A { int x : 5; int y : 2; int z : 1; }; with the idea being that the data fields take the indicated number of bits. In theory this particular structure would require 8 bits or one byte per instance. In practice, because of padding and packing issues, each instance might take more than a byte. Bitfields are sometimes used for interfacing to hardware, for example where specific bits in a machine register have particular meaning. Java has no bitfields. As we discussed in the previous issue of the newsletter, the language is pitched at a somewhat higher level than C and C++, and there's no direct way to manipulate hardware resources. And Java has a variety of types, such as boolean and byte, that support efficient representations of data. INTRODUCTION TO AWT PROGRAMMING PART 2 - ANOTHER EXAMPLE 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(). // detab a String 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. // paint 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. // handle 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. // load a file 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. // constructor 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); // Actually display the window. show(); } // Driver main() method. // driver 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. ACKNOWLEDGEMENTS Thanks to Jay Burgess, Thierry Ciot, Irv Kanode, Mike Paluka, Anand Ramachandran, Srihari Sampathkumar, Jason Sharp, and Bob Shore 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: rmi.net /pub2/glenm/javalett or on the Web at: http://rainbow.rmi.net/~glenm 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) 1997 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: rmi.net /pub2/glenm/javalett (for back issues) Web: http://rainbow.rmi.net/~glenm