BUY THIS BOOK

Safari Books Online

What is this?

Looking to Reprint this content?

Java Cookbook
Java Cookbook Solutions and Examples for Java Developers

By Ian F. Darwin

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: Getting Started: Compiling, Running, and Debugging
This chapter covers some entry-level tasks that you simply need to know how to do before you can go on -- it is said you must crawl before you can walk, and walk before you can ride a bicycle. Before you can try out anything else in the book, you need to be able to compile and run your Java, so I start there, showing several ways: the JDK way, the Mac way, and the Integrated Development Environment (IDE) way. Then I'll discuss a few details about applets, in case you are working on them. Deprecation warnings come next, as you're likely to meet them in maintaining "old" Java code.
If you're already happy with your IDE, you may wish to skip some or all of this material. It's here to ensure that everybody can compile and debug their programs before we move on.
You need to compile and run your Java program.
This is one of the few areas where your computer's operating system impinges into Java's portability, so let's get it out of the way first.

Section 1.2.2.1: JDK

Using the command-line Java Development Kit (JDK) may be the best way to keep up with the very latest improvements from Sun/JavaSoft. This is not the fastest compiler available by any means; the compiler is written in Java and interpreted at compile time, making it a sensible bootstrapping solution, but not necessarily optimal for speed of development. Nonetheless, using Sun's JDK (or Java SDK), the commands are javac to compile and java to run your program. For example:
C:\javasrc>javac HelloWorld.java

C:\javasrc>java HelloWorld
Hello, World

C:\javasrc>
As you can see from the compiler's (lack of) output, this compiler works on the Unix "no news is good news" philosophy: if a program was able to do what you asked it to, it shouldn't bother nattering at you to say that it did so. Many people use this compiler or one of its clones. The javac and java commands are available with the JDK on both Windows and Unix, and under MacOS X if you have installed the bundled Developer Tools package.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Introduction
This chapter covers some entry-level tasks that you simply need to know how to do before you can go on -- it is said you must crawl before you can walk, and walk before you can ride a bicycle. Before you can try out anything else in the book, you need to be able to compile and run your Java, so I start there, showing several ways: the JDK way, the Mac way, and the Integrated Development Environment (IDE) way. Then I'll discuss a few details about applets, in case you are working on them. Deprecation warnings come next, as you're likely to meet them in maintaining "old" Java code.
If you're already happy with your IDE, you may wish to skip some or all of this material. It's here to ensure that everybody can compile and debug their programs before we move on.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Compiling and Running Java: JDK
You need to compile and run your Java program.
This is one of the few areas where your computer's operating system impinges into Java's portability, so let's get it out of the way first.

Section 1.2.2.1: JDK

Using the command-line Java Development Kit (JDK) may be the best way to keep up with the very latest improvements from Sun/JavaSoft. This is not the fastest compiler available by any means; the compiler is written in Java and interpreted at compile time, making it a sensible bootstrapping solution, but not necessarily optimal for speed of development. Nonetheless, using Sun's JDK (or Java SDK), the commands are javac to compile and java to run your program. For example:
C:\javasrc>javac HelloWorld.java

C:\javasrc>java HelloWorld
Hello, World

C:\javasrc>
As you can see from the compiler's (lack of) output, this compiler works on the Unix "no news is good news" philosophy: if a program was able to do what you asked it to, it shouldn't bother nattering at you to say that it did so. Many people use this compiler or one of its clones. The javac and java commands are available with the JDK on both Windows and Unix, and under MacOS X if you have installed the bundled Developer Tools package.
There is an optional setting called CLASSPATH, discussed in Section 2.6, that controls where Java looks for classes. CLASSPATH, if set, is used by both javac and java. In older versions of Java you had to set your CLASSPATH to include "." even to run a simple program from the current directory; this is no longer true on Sun's current Java implementations. It may be true on some of the clones.

Section 1.2.2.2: Command-line alternatives

Sun's javac compiler is the official reference implementation. But it is itself written in Java, and hence must be interpreted at runtime. Recognizing the slowness of compilation as a significant hindrance to developers, Sun's Java folk went back and rewrote the compiler from scratch, discarding some old baggage and using new language features. This new compiler (still named
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Editing and Compiling with a Color-Highlighting Editor
You are tired of command-line tools but not ready for an IDE.
Use a color-highlighting editor.
It's less than an IDE (see the next recipe), but more than a command line. What is it? It's an editor with Java support. Tools such as TextPad (http://www.textpad.com), Visual Slick Edit, and others are low-cost windowed editors (primarily for MS-Windows) that have some amount of Java recognition built in, and the ability to compile from within the editor. TextPad has quite a number of file types that it recognizes, including batch files and shell scripts, C, C++, Java, JSP (see Section 18.7), JavaScript (a client-side web technology), and many others. For each of these, it uses color highlighting to show which part of the file being edited comprises keywords, comments, quoted strings, and so on. This is very useful in spotting when part of your code has been swallowed up by an unterminated /* comment or a missing quote. While this isn't the same as the deep understanding of Java that a full IDE might possess, experience has shown that it definitely aids programmer productivity. TextPad also has a "compile Java" command and a "run external program" command. Both of these have the advantage of capturing the entire command output into a window, which may be easier to scroll than a command-line window on some platforms. On the other hand, you don't see the command results until the program terminates, which can be most uncomfortable if your GUI application throws an exception before it puts up its main window. Despite this minor drawback, TextPad is a very useful tool. Other editors that include color highlighting include vim (an enhanced version of the Unix tool vi, available for MS-Windows and Unix platforms; see http://www.vim.org), the ever-popular Emacs editor, and many others.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Compiling, Running, and Testing with an IDE
Several tools are too many.
Use an integrated development environment.
Many programmers find that using a handful of separate tools -- a text editor, a compiler, and a runner program, not to mention a debugger (see Section 1.13) -- is too many. An integrated development environment (IDE) integrates all of these into a single toolset with a (hopefully consistent) graphical user interface. There are many IDEs to choose from, ranging from text editors that allow you to compile and run a Java program, all the way up to fully integrated tools with their own compilers and virtual machines. Class browsers and other features of IDEs round out the purported ease-of-use feature-sets of these tools. It has been argued many times whether an IDE really makes you more productive or if you just have more fun doing the same thing. However, even the JDK maintainers at Sun admit (perhaps for the benefit of their advertisers) that an IDE is often more productive, although it hides many implementation details and tends to generate code that locks you into a particular IDE. Sun's Java Jumpstart CD (part of Developer Essentials) said, at one time:
The JDK software comes with a minimal set of tools. Serious developers are advised to use a professional Integrated Development Environment with JDK 1.2 software. Click on one of the images below to visit external sites and learn more.
This is followed by some (presumably paid) advertising links to Inprise/Borland JBuilder, WebGain Visual Cafe, and Sybase PowerJ development suites.
I don't plan to debate the IDE versus the command-line process; I'm just going to show a few examples of using a couple of the Java-based IDEs. One that runs on both MS-Windows and Unix platforms is Forte, which is a free download from Sun. Originally created by NetBeans.com, this IDE was so good that Sun bought the company, and now distributes the IDE for free. Forte is also open sourced. You can download the compiled version from http://www.sun.com/forte/ffj/ and the open source version from
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Using Classes from This Book
You want to try out my examples and/or use my utility classes.
Download the latest zip file of the booksource files and unzip it. Install the class JAR file in your CLASSPATH. Or download just the files you need.
You can download the latest version of the source code for all the examples in the book from the book web site, http://javacook.darwinsys.com. You will get two files. First is the source code, in a file called javacooksrc.jar, which you should unzip someplace convenient or wherever you like to keep source code. Second is a file called com-darwinsys-util.jar, which you need to set in your CLASSPATH (see Section 2.6) or JDKHOME/jre/lib/ext directory. The files are roughly organized in per-chapter directories, but there is a lot of overlap and cross-referencing. Because of this, I have prepared a cross-reference file named index-bychapter.html. There is also a mechanically generated file called index-byname.html, which you can use if you know the name of the file you want (and remember that Java source files almost always have the same name as the public class they contain). The canonical index file, index.html, links to both these files.
Once you've set your CLASSPATH, you can compile. In most directories you can simply say javac *.java or jikes *.java . Of course, not everybody likes typing those commands, so there is a makefile for the make utility. make is standard on Unix and readily available for MS-Windows from, for example, the GNUwin32 project (see http://sourceforge.net/projects/gnuwin32/). There is also a top-level makefile that visits the subdirectories and runs make in each of them. These makefiles have been tested with gmake (GNU make 3.79.1), BSD make (OpenBSD 2.8), and they should work with almost any reasonably modern make program or equivalent.
There may also be times when you don't want to download the entire archive -- if you just need a bit of code in a hurry -- so you can access those index files and the resulting directory, for "anyplace, anytime access" on the same web site.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Automating Compilation with jr
You get tired of typing javac and java commands.
Use my jr script.
Although it may be tedious, there is some logic behind the fact that the compilation command (javac, jikes, etc.) requires you to include the filename extension, and the running command (java) requires you to omit the filename extension -- you can't type java HelloWorld.class and have it run the HelloWorld program from the current directory. The compiler is actually reading a source file, while the java command is running a class, a class that might be located someplace in your CLASSPATH (see Section 2.6). It is common for JDK users to use a batch script or command file to automate this. Mine is called jr, for Java compile and Run. The Unix version is jr, a shell script:
javac $1.java && java $*
The $* gets expanded to include $1 and any other arguments. The MS-Windows version is jr.bat :
javac %1.java

if errorlevel 1 goto norun

java  %1   %2 %3 %4 %5 %6

:norun
For people using MS-Windows who have no experience using batch files for compilation, fear not. You could just copy this jr.bat file into the JDKHOME/bin directory. But the problem then is that when you deinstall that JDK version and install a new one, you'd lose jr. What I usually do on MS-Windows is this: just create a directory that won't conflict with anything else, such as C:\bin ("bin" being an old name for binary programs; by tradition all of one's own programs go there). Just add this to your PATH setting, either in your autoexec.bat file or in your Control Panel settings. Copy jr.bat into this directory, and you're done! From then on you can just give commands such as jr HelloWorld. The script will run javac HelloWorld.java for you and, if there are no errors, it will run java HelloWorld.
Feel free to improve upon this and to call it whatever you like.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Automating Compilation with make
You get tired of typing javac and java commands.
Use the make utility to direct your compilations.
The Unix operating system has long had to deal with automating large or repetitive compilations. The most enduring tool for this purpose is make, invented by Stu Feldman at Bell Laboratories in the mid-1970s and still widely used. There have been literally dozens of make -like programs over the years. The X Window System has imake, which is really a front-end to make. Linux and GNU enthusiasts have gmake, and BSD systems feature BSD make; one or another will be installed under the name make. The cygwin32 project features its own make, a version of gmake. make consults a file called Makefile (or makefile ) in the current directory to figure out what you want done and how to do it. A makefile to build one Java program could be as simple as this:
all:
    javac HelloWorld.java
Makefiles can be much more involved. One common feature is to parameterize a makefile so that if you need to port the code to a new platform or you distribute your source code to others to port, all the necessary makefile changes are in one place. For example, to use make variables to let the user compile with either javac or Jikes, and to add a rule to remove the *.class files after a round of debugging, the makefile might grow somewhat, as shown here. Note that lines beginning with the pound sign (#) are comments for the reader and are ignored by make :
# Makefile for Acme FlutterBox program.
# Uncomment one of these compiler definitions:
#JAVAC=   javac
JAVAC=    jikes +E
 
compile:
        $(JAVAC)  *.java
 
clean:
        @rm -f *.class  
All modern Unix systems and most MS-Windows IDEs ship with some version of make. Java became popular after the current fragmentation of Unix into multiple systems maintained by different groups, so many current make programs do not come preconfigured with "convenience" rules for Java; they all come with rules for C and other older languages. Thus you may want to provide a "default" rule for compiling from
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Automating Compilation with Ant
You get tired of typing javac and java commands.
Use the Ant program to direct your compilations.
The intricacies of makefiles and their importabilities have led to the development of a pure-Java solution for automating the build process. Ant is free software; it is available in source form or ready-to-run from the Apache Foundation's Jakarta project web site, at http://jakarta.apache.org/ant/. Like make, Ant uses a file or files -- written in XML -- listing what to do and, if necessary, how to do it. These rules are intended to be platform-independent, though you can of course write platform-specific recipes if necessary.
To use Ant you must create a 15-30 line file specifying various options. This file should be called build.xml ; if you call it anything else, you'll have to give a special command-line arguments every time you run Ant. Example 1-1 shows the build script used to build the files in the starting directory. See Section 21.1 for discussion of the XML syntax. For now, note that the <!- - tag begins an XML comment, which extends to the - -> tag.
Example 1-1. Ant example file (build.xml)
<project name="Java Cookbook Examples" default="compile" basedir=".">

  <!-- set global properties for this build -->
  <property name="src" value="."/>
  <property name="build" value="build"/>
  <!-- Specify the compiler to use. 
    Using jikes is supported but requires rt.jar in classpath. -->
  <property name="build.compiler" value="modern"/>

  <target name="init">
    <!-- Create the time stamp -->
    <tstamp/>
    <!-- Create the build directory structure used by compile -->
    <mkdir dir="${build}"/>
  </target>

  <!-- specify what to compile. This builds everything -->
  <target name="compile" depends="init">

    <!-- Compile the java code from ${src} into ${build} -->
    <javac srcdir="${src}" destdir="${build}"
          classpath="../com-darwinsys-util.jar"/>
  </target>

</project>
When you run Ant, it produces a reasonable amount of notification as it goes, similar to make :
$ ant  compile
Buildfile: build.xml
Project base dir set to: /home/ian/javasrc/starting
Executing Target: init
Executing Target: compile
Compiling 19 source files to /home/ian/javasrc/starting/build
Performing a Modern Compile
Copying 22 support files to /home/ian/javasrc/starting/build
Completed in 8 seconds
$ 
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Running Applets
You want to run an applet.
Write some HTML and point a browser at it.
An applet is simply a Java class that extends java.applet.Applet, and in doing so inherits the functionality it needs to be viewable inside a web page in a Java-enabled web browser. All that's necessary is an HTML page referring to the applet. This HTML page requires a minimum of three attributes , or modifiers: the applet itself, and the width and height it needs on-screen, in screen dots or pixels. This is not the place for me to teach you the syntax of HTML -- there is some of that in Section 17.2 -- but I'll show my HTML applet template file. Many of the IDEs will write a page like this for you if you use their "build new applet" wizards.
<HTML>
<HEAD><TITLE>A Demonstration</TITLE></HEAD>
<BODY>
<H1>My TEMPLATE Applet</H1>
<APPLET CODE="CCC.class"  WIDTH="200" HEIGHT="200">
</APPLET>
</BODY>
</HTML>
You can probably intuit from this just about all you need to get started. For a little more detail, see Section 17.2. Once you've created this file (replacing the CCC with the actual name of your applet) and placed it in the same directory as the class file, you need only tell the browser to view the HTML page, and the applet should be included in it.
All right, so the applet appeared and it even almost worked. Make a change to the Java source and recompile. Click the browser's Reload button. Chances are you're still running the old version! Browsers aren't very good at debugging applets. You can sometimes get around this by holding down the Shift key while you click Reload. But to let you be sure, there is a program in the JDK known as Appletviewer , a kind of mini-browser. You need to give it the HTML file, just like a regular browser. Sun's AppletViewer (shown in Figure 1-12 under MS-Windows) has an explicit reload button that actually reloads the applet. And it has other features such as debugging hooks and other information displays. It also has a View->Tag menu that lets you resize the window until the applet looks best, and then you can copy and paste the tag -- including the adjusted WIDTH and HEIGHT tags -- into a longer HTML document.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Dealing with Deprecation Warnings
Your code used to compile cleanly, but now gives deprecation warnings.
You must have blinked :-). Either live with the warnings -- live dangerously -- or revise your code to eliminate the warnings.
Each new release of Java includes a lot of powerful new functionality, but at a price: during the evolution of this new stuff, Java's maintainers find some old stuff that wasn't done right and shouldn't be used anymore because they can't really fix it. In building JDK 1.1, for example, they realized that the java.util.Date class had some serious limitations with regard to internationalization. Accordingly, many of the Date class methods and constructors are marked "deprecated." To deprecate something means, according to my Concise Oxford Dictionary of Current English, to "express wish against or disapproval of." Java's developers are therefore expressing a wish that you no longer do things the old way. Try compiling this code:
import java.util.Date;

/** Demonstrate deprecation warning */
public class Deprec {

    public static void main(String[] av) {

        // Create a Date object for May 5, 1986
        // EXPECT DEPRECATION WARNING
        Date d = new Date(86, 04, 05);        // May 5, 1986
        System.out.println("Date is " + d);
    }
}
What happened? When I compile it on Java 2, I get this warning:
C:\javasrc>javac Deprec.java
Note: Deprec.java uses or overrides a deprecated API.  Recompile with 
"-deprecation" for details.
1 warning
C:\javasrc>
So, we follow orders. Recompile with -deprecation for details:
C:\javasrc>javac -deprecation Deprec.java
Deprec.java:10: warning: constructor Date(int,int,int) in class java.util.Date has 
been deprecated
                Date d = new Date(86, 04, 05);          // May 5, 1986
                         ^
1 warning

C:\javasrc>
The warning is simple: the Date constructor that takes three integer arguments has been deprecated. How do you fix it? The answer is, as in most questions of usage, to refer to the Javadoc documentation for the class. In Java 2, the introduction to the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Conditional Debugging without #ifdef
You want conditional compilation and Java doesn't seem to provide it.
Use constants or command-line arguments, depending upon the goal.
Some older languages such as C, PL/I, and C++ provide a feature known as conditional compilation. Conditional compilation means that parts of the program can be included or excluded at compile time based upon some condition. One thing it's often used for is to include or exclude debugging print statements. When the program appears to be working, the developer is struck by a fit of hubris and removes all the error checking :-). A more common rationale is that the developer wants to make the finished program smaller -- a worthy goal -- or run faster by removing conditional statements.
Although Java lacks any explicit conditional compilation, there is a kind of conditional compilation implicit in the language. All Java compilers must do flow analysis to ensure that all paths to a local variable's usage pass through a statement that assigns it a value first, that all returns from a function pass out via someplace that provides a return value, and so on. Imagine what the compiler will do when it finds an if statement whose value is known to be false at compile time. Why should it even generate code for the condition? True, you say, but how can the results of an if statement be known at compile time? Simple: through final boolean variables. Further, if the value of the if condition is known to be false, then the body of the if statement should not be emitted by the compiler either. Presto -- instant conditional compilation!
// IfDef.java
final boolean DEBUG = false;
System.out.println("Hello, World ");
if (DEBUG) {
        System.out.println("Life is a voyage, not a destination");
}
Compilation of this program and examination of the resulting class file reveals that the string "Hello" does appear, but the conditionally printed epigram does not. The entire
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Debugging Printouts
You want to have debugging statements left in your code to be enabled at runtime.
Use my Debug class.
Instead of using the conditional compilation mechanism of Section 1.11, you may want to leave your debugging statements in the code, but enable them only at runtime, when a problem surfaces. This is a good technique for all but the most compute-intensive applications because the overhead of a simple if statement is not all that great. Let's combine the flexibility of runtime checking with the simple if statement to debug a hypothetical fetch( ) method (part of Fetch.java):
String name = "poem";
if (System.getProperty("debug.fetch") != null) {
    System.err.println("Fetching " + name);
}
value = fetch(name);
Then, we can compile and run this normally and the debugging statement will be omitted. But if we run it with a -D argument to enable debug.fetch, the printout will occur:
> java Fetch          # See? No output
> java -Ddebug.fetch Fetch
Fetching poem
>
Of course this kind of if statement is tedious to write in large quantities, so I have encapsulated it into a Debug class, which is part of my com.darwinsys.util package. Debug.java appears in full at the end of this chapter, in Section 1.19. My Debug class also provides the string "debug". as part of the System.getProperty( ) , so we can simplify the previous Fetch example as follows (code in FetchDebug.java):
String name = "poem", value;
Fetch f = new Fetch(  );
Debug.println("fetch", "Fetching " + name);
value = f.fetch(name);  
Running it behaves identically to the original Fetch:
> java FetchDebug     # again, no output
> java -Ddebug.fetch FetchDebug
Fetching poem
> 
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Using a Debugger
That debugging printout code is still not enough.
Use a debugger.
The JDK includes a command-line-based debugger, jdb, and there are any number of IDEs that include their own debugging tools. If you've focused on one IDE, learn to use the debugger that it provides. If you're a command-line junkie like me, you may want to learn at least the basic operations of jdb.
Here is a buggy program. It has intentionally had bugs introduced so that you can see their effects in a debugger.
/** This program exhibits some bugs, so we can use a debugger */
public class Buggy {
    static String name;

    public static void main(String[] args) {
        int n = name.length(  );    // bug # 1

        System.out.println(n);

        name += "; The end.";    // bug #2
        System.out.println(name); // #3
    }
}
Here is a session using jdb to find these bugs:
ian> java Buggy
Exception in thread "main" java.lang.NullPointerException
        at Buggy.main(Compiled Code)
ian> jdb Buggy
Initializing jdb...
0xb2:class(Buggy)
> run
run Buggy 
running ...
main[1] 
Uncaught exception: java.lang.NullPointerException
        at Buggy.main(Buggy.java:6)
        at sun.tools.agent.MainThread.runMain(Native Method)
        at sun.tools.agent.MainThread.run(MainThread.java:49)

main[1] list
2          public class Buggy {
3               static String name;
4          
5               public static void main(String[] args) {
6       =>              int n = name.length(  );  // bug # 1
7          
8                       System.out.println(n);
9          
10                      name += "; The end.";   // bug #2
main[1] print Buggy.name
Buggy.name = null
main[1] help
** command list **
threads [threadgroup]     -- list threads
thread <thread id>        -- set default thread
suspend [thread id(s)]    -- suspend threads (default: all)
resume [thread id(s)]     -- resume threads (default: all)
where [thread id] | all   -- dump a thread's stack
wherei [thread id] | all  -- dump a thread's stack, with pc info
threadgroups              -- list threadgroups
threadgroup <name>        -- set current threadgroup

print <id> [id(s)]        -- print object or field
dump <id> [id(s)]         -- print all object information

locals                    -- print all local variables in current stack frame

classes                   -- list currently known classes
methods <class id>        -- list a class's methods

stop in <class id>.<method>[(argument_type,...)] -- set a breakpoint in a method
stop at <class id>:<line> -- set a breakpoint at a line
up [n frames]             -- move up a thread's stack
down [n frames]           -- move down a thread's stack
clear <class id>.<method>[(argument_type,...)]   -- clear a breakpoint in a method
clear <class id>:<line>   -- clear a breakpoint at a line
step                      -- execute current line
step up                   -- execute until the current method returns to its caller
stepi                     -- execute current instruction
next                      -- step one line (step OVER calls)
cont                      -- continue execution from breakpoint

catch <class id>          -- break for the specified exception
ignore <class id>         -- ignore when the specified exception

list [line number|method] -- print source code
use [source file path]    -- display or change the source path

memory                    -- report memory usage
gc                        -- free unused objects

load classname            -- load Java class to be debugged
run <class> [args]        -- start execution of a loaded Java class
!!                        -- repeat last command
help (or ?)               -- list commands
exit (or quit)            -- exit debugger
main[1] exit
ian>
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Unit Testing: Avoid the Need for Debuggers
You don't want to have to debug your code.
Use unit testing to validate each class as you develop it.
Stopping to use a debugger is time-consuming. Better to test beforehand. The methodology of unit testing has been around for a long time, but has been overshadowed by newer methodologies. Unit testing is a tried and true means of getting your code tested in small pieces. Typically, in an OO language like Java, unit testing is applied to individual classes, in contrast to "black box" testing where the entire application is tested.
I have long been an advocate of this very basic testing methodology. Indeed, developers of the software methodology known as Extreme Programming (XP for short; see http://www.extremeprogramming.org) advocate writing the unit tests before you write the code, and also advocate running your tests almost every time you compile. This group of extremists has some very well-known leaders, including Gamma and Beck of Design Patterns fame. While I am not yet ready to unconditionally endorse all aspects of Extreme Programming, I certainly go along with their advocacy of unit testing.
Indeed, many of my classes come with a "built-in" unit test. Classes that are not main programs in their own right often include a main method that just tests out the functionality of the class. Here is an example:
/** A simple class used to demonstrate unit testing. */
public class Person {
    protected String fullName;
    protected String firstName, lastName;

    /** Construct a Person using his/her first+last names. */
    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    /** Get the person's full name */
    public String getFullName(  ) {
        if (fullName != null)
            return fullName;
        return firstName + " " + lastName;
    }

    /** Simple test program. */
    public static void main(String[] argv) {
        Person p = new Person("Ian", "Darwin");
        String f = p.getFullName(  );
        if (!f.equals("Ian Darwin"))
            throw new IllegalStateException("Name concatenation broken");
        System.out.println("Fullname " + f + " looks good");
    }
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Decompiling Java Class Files
You lost the source code.
If you still have the class files, decompile them.
Have you ever looked at a class file by accident? Open it in a text editor, for example, and you might see this. You've never done this by accident, right? Sure, I believe you . . .
^H^@Z^C^@^@^@P^H^@[^H^@n^H^@o^H^@p^H^@q^H^@r^H^@s^H^@t^H^@v^H^@y^H
^@z^H^@{^H^@}^H^@Ç^H^@ä^H^@à^H^@á^H^@ª^H^@º^G^@ç^G^@Æ^G^@ô^G^@ö^G^@ò^G^@Û^G^@ù^G
^@ÿ^G^@...^G^@Ü^G^@¢^G^@£^G^@¥
^@^V^@@
^@^\^@@
^@!^@A
^@^Y^@B
^@^[^@C
There's no resemblance to the Java source file that you wrote and spent so long fussing over the formatting of. What did it get you? Nothing here. The class file is a binary file that can't be inspected easily. However, it is in a well-documented format, and there's the rub. Once a format is known, files can be examined. One example of a Java program that examines other Java programs is javap, which gives you the external view of a class file. I'll show you in Section 25.3 just how this part of javap works and how you can write your own tools that process other Java classes. Meanwhile, this discussion is about decompilation. Let's suppose you have put some meat through a meat grinder. It's been converted to zillions of little bits. It might, in fact, look a bit like the class file seen here. Now suppose that unbeknownst to you, your paycheck fell into the meat and went through the grinder. Ugh! But the real question is, can you put the paycheck back together from the little pieces in the output? A related question is whether you can put a Java source file back together from the little pieces in the class file.
The task seems impossible. The file appears inscrutable. How can it be un-ground? But computer geeks like to work with files, and restoring structure to them is one part of that. When the infamous Internet Worm struck in 1988, it was only a matter of hours before security experts had taken the binary compiled program -- most OSes' equivalent of a class file -- and turned it back into source code without any tools other than debuggers, dumps, and manuals. So it is possible to take an object file and turn it back into some kind of source file. Now the ground-up paycheck, if you find the pieces and tape it back together, will still have bumps (not to mention the smell of salami or pastrami as appropriate). And a decompiled file will have one major bump: no comments! All the comments will be gone. But hopefully you can get back something that will take the place of your lost source file.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Preventing Others from Decompiling Your Java Files
But I don't want people to be able to decompile my Java programs!
Obfuscate them.
It has been said that for any weapon there is a defense, and for any defense there is a weapon. If the weapon is a decompiler, then the defense is something called an obfuscator . An obfuscator takes your program and tries to make it obscure, so that decompilation either will not work or will not be useful.
Because Mr. van Vliet, the late inventor of Mocha, did not release its source code, nobody else can take it over and maintain it, as we don't have the source. Or do we? Of course! That's it! We'll just run it through itself. Well, if you can download a copy, you can try it. But what you'll find is that it doesn't work. The entire program has been obfuscated. Yes, Mr. van Vliet also wrote the first Java obfuscator, partly in reaction to all the people who flamed him on the Net for releasing Mocha. Due to his untimely death, his obfuscator is no longer available.
There are, of course, commercial obfuscation programs that will do some degree of obfuscation. Some of them actually encrypt the file and use a custom class loader to decrypt it at runtime. I suppose if you wanted to keep people from learning how your program worked, which you well might for commercial or other reasons, you'd want to use one of these tools. Again, a Java resource web site or a current Java developer's magazine would be the place to go for the latest versions.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Getting Readable Tracebacks
You're getting an exception stack trace at runtime, but most of the important parts don't have line numbers.
Disable JIT and run it again. Or use the current HotSpot runtime.
When a Java program throws an exception, the exception propagates up the call stack until there is a catch clause that matches it. If none is found, the Java interpreter program catches it and prints a stack traceback showing all the method calls that got from the top of the program to the place where the exception was thrown. You can print this traceback yourself in any catch clause: the Throwable class has several methods called printStackTrace( ).
The Just-In-Time (JIT) translation process consists of having the Java runtime convert part of your compiled class file into machine language, so that it can run at full execution speed. This is a necessary step for making Java programs run under interpretation and still be acceptably fast. However, until recently its one drawback was that it generally lost the line numbers. Hence, when your program died, you still got a stack traceback but it no longer showed the line numbers where the error occurred. So we have the tradeoff of making the program run faster, but harder to debug. The latest versions of Sun's Java runtime include the HotSpot Just-In-Time translator, which doesn't have this problem.
If you're still using an older (or non-Sun) JIT, there is a way around this. If the program is getting a stack traceback and you want to make it readable, you need only disable the JIT processing. How you do this depends upon what release of Java you are using. In the JDK 1.2 (Java 2), you need only set the environment variable JAVA_COMPILER to the value NONE, using the appropriate set command.
C:\> set JAVA_COMPILER=NONE     # DOS, MS-Windows
setenv JAVA_COMPILER NONE                # UNIX Csh
export JAVA_COMPILER=NONE     # UNIX Ksh, modern sh
To make this permanent, you would set it in the appropriate configuration file on your system; on Windows NT, you could also set this in the System Control Panel. You might well wish to make this setting the default, since using the JIT does take longer for startup, in return for faster execution. I ran JabaDex, my personal information manager application (see
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Finding More Java Source Code
You want even more Java code examples to look at.
Use The Source, Luke.
Java source code is everywhere. As mentioned in the Preface, all the code examples from this book can be downloaded from the O'Reilly site (http://java.oreilly.com). What I didn't tell you, but what you might have realized by extension, is that the source examples from all the O'Reilly Java books are available there too: the Java Examples in a Nutshell book; the Java Swing book; all of them.
Another valuable resource is the source code for the Java API. You may not have realized it, but the source code for all the public parts of the Java API are included with each release of the Java Development Kit. Want to know how java.util.ArrayList actually works? You have the source code. Got a problem making a JTable behave? Sun's JDK includes the source for all the public classes! Look for a file called src.zip or src.jar ; some versions unzip this and some do not.
If that's not enough, you can get the source for all of the JDK for free over the Internet just by committing to the Sun Java Community Source License and downloading a large file. This includes the source for the public and non-public parts of the API, as well as the compiler (written in Java) and a large body of code written in "native" code ( C/C++): the runtime itself and the interfaces to the native library. For example, java.io.Reader has a method called read( ) , which reads bytes of data from a file or network connection. This is written in C because it actually calls the read( ) system call for Unix, MS-Windows, MacOS, Palm, BeOS, or whatever. The JDK source kit includes the source of all this stuff.
And ever since the early days of Java, there have been a number of web sites set up to distribute free-software or open source Java, just as with most other modern "evangelized" languages such as Perl, Python, Tk/Tcl, and others. (In fact, if you need native code to deal with some oddball filesystem mechanism in a portable way, beyond the material in Chapter 10 of this book, the source code for the above-mentioned languages' runtime systems might be a good place to look.)
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Program: Debug
Most of the chapters of this book will end with a "Program" recipe that illustrates some aspect of the material covered in the chapter. Example 1-2 is the source code for the Debug utility mentioned in Section 1.12.
Example 1-2. Debug.java
package com.darwinsys.util;

/** Utilities for debugging
 */
public class Debug {
    /** Static method to see if a given category of debugging is enabled.
     * Enable by setting e.g., -Ddebug.fileio to debug file I/O operations.
     * Use like this:<BR>
     * if (Debug.isEnabled("fileio"))<BR>
     *     System.out.println("Starting to read file " + fileName);
     */
    public static boolean isEnabled(String category) {
        return System.getProperty("debug." + category) != null;
    }

    /** Static method to println a given message if the
     * given category is enabled for debugging.
     */
    public static void println(String category, String msg) {
        if (isEnabled(category))
            System.out.println(msg);
    }
    /** Same thing but for non-String objects (think of the other
     * form as an optimization of this).
     */
    public static void println(String category, Object stuff) {
        println(category, stuff.toString(  ));
    }
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: Interacting with the Environment
This chapter describes how your Java program can deal with its immediate surroundings, what we call the runtime environment . In one sense, everything you do in a Java program using almost any Java API involves the environment. Here we focus more narrowly on things that directly surround your program. Along the way we'll meet the System class, which knows a lot about our system.
Two other runtime classes deserve brief mention. The first, java.lang.Runtime, lies behind many of the methods in the System class. System.exit( ), for example, just calls Runtime.exit( ). This is technically part of "the environment," but the only time we use it directly is to run other programs, which is covered in Section 26.2. The java.awt.Toolkit object is also part of the environment and is discussed in Chapter 12.
You want to get at environment variables from within your Java program.
Don't.
The seventh edition of Unix, released in 1979, had an exciting new feature known as environment variables. Environment variables are in all modern Unix systems and in most later command-line systems such as the DOS subsystem underlying MS-Windows, but are not in Macintosh computers, Palm Pilots, SmartCards, or other Java environments. Environment variables are commonly used for customizing an individual computer user's runtime environment, hence the name. To take one example that will be familiar to most readers, on Unix or DOS the environment variable PATH determines where the system will look for executable programs. So of course the issue comes up: "How do I get at environment variables from my Java program?"
The answer is that you can do this in some versions of Java, but you shouldn't. Java is designed to be a portable runtime environment. As such, you should not depend on operating system features that don't exist on every single Java platform. I just mentioned several Java platforms that don't have environment variables.
Oh, all right, if you insist. There is a static method called getenv( ) in class java.lang.System . Let's try it out. But remember, you made me do it. First, the code. All we need is this line in a main program:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Introduction
This chapter describes how your Java program can deal with its immediate surroundings, what we call the runtime environment . In one sense, everything you do in a Java program using almost any Java API involves the environment. Here we focus more narrowly on things that directly surround your program. Along the way we'll meet the System class, which knows a lot about our system.
Two other runtime classes deserve brief mention. The first, java.lang.Runtime, lies behind many of the methods in the System class. System.exit( ), for example, just calls Runtime.exit( ). This is technically part of "the environment," but the only time we use it directly is to run other programs, which is covered in Section 26.2. The java.awt.Toolkit object is also part of the environment and is discussed in Chapter 12.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Getting Environment Variables
You want to get at environment variables from within your Java program.
Don't.
The seventh edition of Unix, released in 1979, had an exciting new feature known as environment variables. Environment variables are in all modern Unix systems and in most later command-line systems such as the DOS subsystem underlying MS-Windows, but are not in Macintosh computers, Palm Pilots, SmartCards, or other Java environments. Environment variables are commonly used for customizing an individual computer user's runtime environment, hence the name. To take one example that will be familiar to most readers, on Unix or DOS the environment variable PATH determines where the system will look for executable programs. So of course the issue comes up: "How do I get at environment variables from my Java program?"
The answer is that you can do this in some versions of Java, but you shouldn't. Java is designed to be a portable runtime environment. As such, you should not depend on operating system features that don't exist on every single Java platform. I just mentioned several Java platforms that don't have environment variables.
Oh, all right, if you insist. There is a static method called getenv( ) in class java.lang.System . Let's try it out. But remember, you made me do it. First, the code. All we need is this line in a main program:
System.out.println("System.getenv(\"PATH\") = " + System.getenv("PATH"));
Let's try compiling it:
C:\javasrc>javac GetEnv.java
Note: GetEnv.java uses or overrides a deprecated API. Recompile with -deprecation 
for details.
That message is seldom welcome news. We'll do as it says:
C:\javasrc>javac -deprecation GetEnv.java
GetEnv.java:9: Note: The method java.lang.String getenv(java.lang.String) in class
 java.lang.System has been deprecated.
System.out.println("System.getenv(\"PATH\") = " + System.getenv("PATH"));
                                                         ^
Note: GetEnv.java uses or overrides a deprecated API.  Please consult the 
documentation for a better alternative.
1 warning
But it's only a warning, right? What the heck. Let's try running the program!
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
System Properties
You need to get information from the system properties.
Use System.getProperty( ) or System.getProperties( ).
What is a property anyway? A property is just a name and value pair stored in a java.util.Properties object, which we'll discuss more fully in Section 7.8. So if I chose to, I could store the following properties in a Properties object called ian:
name=Ian Darwin
favorite_popsicle=cherry
favorite_rock group=Fleetwood Mac
favorite_programming_language=Java
pencil color=green
The Properties class has several forms of its retrieval method. You could, for example, say ian.getProperty("pencil color") and get back the string "green". You can also provide a default: say ian.getProperty("pencil color", "black"), and if the property has not been set you would get the default value "black".
For now, we're concerned with the System class and its role as keeper of the particular Properties object that controls and describes the Java runtime. The System class has a static Properties member whose content is the merger of operating system specifics (os.name, for example), system and user tailoring (java.class.path), and properties defined on the command line (as we'll see in a moment). Note that the use of periods in these names (like os.arch, os.version and java.class.path, java.lang.version) makes it look as though there is a hierarchical relationship similar to that for class names. The Properties class, however, imposes no such relationships: each key is just a string, and dots are not special.
To retrieve one system-provided property, use System.getProperty( ). If you want them all, use System.getProperties( ). Accordingly, if I wanted to find out if the System Properties had a property named "pencil color", I could say:
String color = System.getProperty("pencil color");
But what will that return? Surely Java isn't clever enough to know about everybody's favorite pencil color? Right you are! But we can easily tell Java about our pencil color (or anything else we want to tell it) using the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Writing JDK Release-Dependent Code
You need to write code that depends on the JDK release.
Don't do this.
Although Java is meant to be portable, there are some significant variations in Java runtimes. Sometimes you need to work around a feature that may be missing in older runtimes, but want to use it if it is present. So one of the first things you want to know is how to find out the JDK release corresponding to the Java runtime. This is easily obtained with System.getProperty( ):
System.out.println(System.getProperty("java.specification.version"));
Running this on Java 2 prints "1.2", as in JDK 1.2. Alas, not everyone is completely honest. Kaffe 1.5 certainly has some features of Java 2, but it is not yet a complete implementation of the Java 2 libraries. Yet it happily reports itself as "1.2" also. Caveat hactor!
Accordingly, you may want to test for the presence or absence of particular classes. One way to do this is with Class.forName("class") , which throws an exception if the class cannot be loaded -- a good indication that it's not present in the runtime's library. Here is code for this, from an application wanting to find out whether the JDK 1.1 or later components are available:
/** Test for JDK >= 1.1 */
public class TestJDK11 {
    public static void main(String[] a) {
            // Check for JDK >= 1.1
        try {
            Class.forName("java.lang.reflect.Constructor");
        } catch (ClassNotFoundException e) {
            String failure = 
                "Sorry, but this version of MyApp needs \n" +
                "a Java Runtime based on Java JDK 1.1 or later";
            System.err.println(failure);
            throw new IllegalArgumentException(failure);
        }
        System.out.println("Happy to report that this is JDK1.1");
        // rest of program would go here...
        return;
    }
}
To check if the runtime includes the Swing components with their final names, you could use:
Class.forName("javax.swing.JButton");
It's important to distinguish between testing this at compile time and at runtime. In both cases, this code must be compiled on a system that includes the classes you are testing for -- JDK 1.1 and Swing, respectively. These tests are only attempts to help the poor backwaters Java runtime user trying to run your up-to-date application. The goal is to provide this user with a message more meaningful than the simple "class not found" error that the runtime will give. It's also important to note that this test becomes unreachable if you write it inside any code that depends on the code you are testing for. The check for Swing won't ever see the light of day on a JDK 1.1 system if you write it in the constructor of a
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Writing Operating System-Dependent Code
You need to write code that depends on the underlying operating system.
Again, don't do this. Or, if you must, use System.properties.
While Java is designed to be portable, there are some things that aren't. These include such variables as the filename separator. Everybody on Unix knows that the filename separator is a slash character ( / ) and that a backwards slash or backslash ( \ ) is an escape character. Back in the late 1970s, a group at Microsoft was actually working on Unix -- their version was called Xenix, later taken over by SCO -- and the people working on DOS saw and liked the Unix filesystem model. MS-DOS 2.0 didn't have directories, it just had "user numbers" like the system it was a clone of, Digital Research CP/M (itself a clone of various other systems). So the Microsoft folk set out to clone the Unix filesystem organization. Unfortunately, they had already committed the slash character for use as an option delimiter, for which Unix had used a dash (-). And the PATH separator (:) was also used as a "drive letter" delimiter, as in C: or A:. So we now have commands like this:
System
Directory list command
Meaning
Example PATH setting
Unix
ls -R /
Recursive listing of /, the top-level directory
PATH=/bin:/usr/bin
DOS
dir/s \
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Using CLASSPATH Effectively
You need to keep your class files in a common directory or you're wrestling with CLASSPATH.
Set CLASSPATH to the list of directories and/or JAR files that contain the classes you want.
CLASSPATH is one of the more interesting aspects of using Java. You can store your class files in any of a number of directories, JAR files, or zip files. Just like the PATH your system uses for finding programs, the CLASSPATH is used by the Java runtime to find classes. Even when you type something as simple as java HelloWorld, the Java interpreter looks in each of the places named in your CLASSPATH until it finds a match. Let's work through an example.
The CLASSPATH can be set as an environment variable on systems that support this (at least Unix and MS-Windows). You set it in the same syntax as your PATH environment variable. PATH is a list of directories to look in for programs; CLASSPATH is a list of directories or JAR files to look in for classes.
Alternatively, you can set your CLASSPATH right on the command line:
java -classpath \c:\ian\classes MyProg
Suppose your CLASSPATH were set to C:\classes;. on MS-Windows, or ~/classes:. on Unix (on the Mac, you can set the CLASSPATH with JBindery). Suppose you had just compiled a file named HelloWorld.java into HelloWorld.class, and went to run it. On Unix, if you run one of the kernel tracing tools (trace, strace, truss, ktrace) you would probably see the Java program open (or stat, or access) the following files:
  • Some fil