In this section, we’ll say a
few words about javac
, the Java compiler that is
supplied as part of
Sun’s SDK. (If you are happily
working in another development environment, you may want to skip
ahead to the next section.) The javac
compiler is
written entirely in Java, so it’s available for any platform
that supports the Java runtime system. The ability to support its own
development environments is an important stage in a language’s
development. Java makes this bootstrapping automatic by supplying a
ready-to-run compiler at the same cost as porting the interpreter.
javac
turns Java
source code into a compiled class
that contains Java virtual machine byte-code. By convention, source
files are named with a .java
extension; the
resulting class files have a
.class
extension. Each source code file is a
single compilation unit. As you’ll see in Chapter 6, classes in a given compilation unit share
certain features, such as package
and
import
statements.
javac
allows you one
public class per file and insists the file have the same name as the
class. If the filename and class name don’t match,
javac
issues a compilation error. A single file
can contain multiple classes, as long as only one of the classes is
public. Avoid packing many classes into a single source file.
Including non-public classes in a .java
file is
one easy way to tightly couple such classes to a public class. But
you might also consider using inner classes (see Chapter 6).
Now for an example. Place the following source code in file BigBird.java:
package animals.birds; public class BigBird extends Bird { ... }
Then compile it with:
% javac BigBird.java
Unlike the Java interpreter, which takes just a class name as its
argument, javac
needs a filename to
process. The previous command produces the class
file BigBird.class
in the same directory as the
source file. While it’s useful to have the class file in the
same directory as the source for testing a simple example, for most
real applications you’ll need to store the class file in an
appropriate place in the class path.
You can
use the -d
option to javac
to
specify an alternative directory for storing the class files it
generates. The specified directory is used as the root of the class
hierarchy, so .class
files are placed in this
directory or in a subdirectory below it, depending on whether the
class is contained in a package. (The compiler creates intermediate
subdirectories automatically, if necessary.) For example, we can use
the following command to create the
BigBird.class
file at
/home/vicky/Java/classes/animals/birds/BigBird.class
:
% javac -d /home/vicky/Java/classes BigBird.java
You can specify multiple .java
files in a single
javac
command; the compiler creates a class file
for each source file. But you don’t need to list source files
for other classes that your class references, as long as the other
classes have already been compiled. During compilation, Java resolves
other class references using the class path. If our class refers to
other classes in animals.birds
or other packages,
the appropriate paths should be listed in the class path at compile
time, so that javac
can find the appropriate class
files.
The Java compiler is more intelligent
than your average compiler, replacing some of the functionality of a
make
utility. For example,
javac
compares the modification times of the
source and class files for all referenced classes and recompiles them
as necessary. A compiled Java class remembers the source file from
which it was compiled, so as long as the source file is in the same
directory as the class file, javac
can recompile
the source if necessary. If, in the previous example, class
BigBird
references another class,
animals.furry.Grover
, javac
looks for the source file Grover.java
in an
animals.furry
package and recompiles it if
necessary to bring the Grover.class
class file
up-to-date.
By default, however, javac
checks only source
files that are referenced directly from other source files. This
means that if you have an out-of-date class file that is referenced
only by an up-to-date class file, it may not be noticed and
recompiled. You can force javac
to walk the entire
graph of objects using the
-depend
option. But be warned, this can
increase compilation time significantly. And this technique still
won’t help if you want to keep class libraries or other
collections of classes up to date even if they aren’t being
referenced at all. For that you should consider a make utility.
Finally, it’s important to note that javac
can compile an application even if only the compiled versions of
referenced classes are available. You don’t need source code
for all of your objects. Java class files contain all the data type
and method signature information that source files contain, so
compiling against binary class files is as type-safe (and
exception-safe) as compiling with Java
source code.