Chapter 3. Tools of the Trade

While you will almost certainly do the majority of your Java development in an IDE such as Eclipse, VS Code, or (the author’s favorite) Intellij IDEA, all of the core tools you need to build Java applications are included in the Java Development Kit (JDK) that you have likely already downloaded in “Installing the JDK” from Oracle or another OpenJDK provider.1 In this chapter, we’ll discuss some of these command-line tools that you can use to compile, run, and package Java applications. There are many additional developer tools included in the JDK that we’ll discuss throughout this book.

For more details on IntelliJ IDEA and instructions for loading all of the examples in this book as a project, see Appendix A.

JDK Environment

After you install Java, the core java runtime command may appear in your path (available to run) automatically. However, many of the other commands provided with the JDK may not be available unless you add the Java bin directory to your execution path. The following commands show how to do this on Linux, Mac OS X, and Windows. You will, of course, have to change the path to match the version of Java you have installed.

# Linux
export JAVA_HOME=/usr/lib/jvm/java-12-openjdk-amd64
export PATH=$PATH:$JAVA_HOME/bin

# Mac OS X
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-12.jdk/Contents/Home
export PATH=$PATH:$JAVA_HOME/bin

# Windows
set JAVA_HOME=c:Program FilesJavajdk12
set PATH=%PATH%;%JAVA_HOME%in

On Mac OS X, the situation may be more confusing because recent versions ship with “stubs” for the Java commands installed. If you attempt to run one of these commands, the OS will prompt you to download Java at that time. You can preemptively grab the OpenJDK from Oracle following the instructions in “Java Tools and Environment”.

When in doubt, your go-to test for determining which version of the tools you are using is to use the -version flag on the java and javac commands:

java -version

# openjdk version "12" 2019-03-19
# OpenJDK Runtime Environment (build 12+33)
# OpenJDK 64-Bit Server VM (build 12+33, mixed mode, sharing)

javac -version

# javac 12

The Java VM

A Java virtual machine (VM) is software that implements the Java runtime system and executes Java applications. It can be a standalone application like the java command that comes with the JDK or built into a larger application like a web browser. Usually the interpreter itself is a native application, supplied for each platform, which then bootstraps other tools written in the Java language. Tools such as Java compilers and IDEs are often implemented directly in Java to maximize their portability and extensibility. Eclipse, for example, is a pure-Java application.

The Java VM performs all the runtime activities of Java. It loads Java class files, verifies classes from untrusted sources, and executes the compiled bytecode. It manages memory and system resources. Good implementations also perform dynamic optimization, compiling Java bytecode into native machine instructions.

Running Java Applications

A standalone Java application must have at least one class containing a method called ++main()++, which is the first code to be executed upon startup. To run the application, start the VM, specifying that class as an argument. You can also specify options to the interpreter as well as arguments to be passed to the application__:__
    % java [interpreter options] class_name [program arguments]

The class should be specified as a fully qualified class name, including the package name, if any. Note, however, that you don’t include the .class file extension. Here are a couple of examples:

    %java animals.birds.BigBird
    %java MyTest

The interpreter searches for the class in the classpath, a list of directories and archive files where classes are stored. We’ll discuss the classpath in detail in the next section. The classpath can be specified either by an environment variable or with the command-line option -classpath. If both are present, the command-line option is used.

Alternately, the java command can be used to launch an “executable” Java archive (JAR) file:

    % java -jar spaceblaster.jar

In this case, the JAR file includes metadata with the name of the startup class containing the main() method, and the classpath becomes the JAR file itself.

After loading the first class and executing its main() method, the application can reference other classes, start additional threads, and create its user interface or other structures, as shown in Figure 3-1.

lj5e 0301
Figure 3-1. Starting a Java application

The main() method must have the right method signature. A method signature is the set of information that defines the method. It includes the method’s name, arguments, and return type, as well as type and visibility modifiers. The main() method must be a public, static method that takes an array of String objects as its argument and does not return any value (void):

    public static void main ( String [] myArgs )

The fact that main() is a public and static method simply means that it is globally accessible and that it can be called directly by name. We’ll discuss the implications of visibility modifiers such as public and the meaning of static in Chapters Chapter 4 and Chapter 5.

The main() method’s single argument, the array of String objects, holds the command-line arguments passed to the application. The name of the parameter doesn’t matter; only the type is important. In Java, the content of myArgs is an array. (More on arrays in Chapter 4.) In Java, arrays know how many elements they contain and can happily provide that information:

    int numArgs = myArgs.length;

myArgs[0] is the first command-line argument, and so on.

The Java interpreter continues to run until the main() method of the initial class file returns and until any threads that it has started also exit. (More on threads in Chapter 9.) Special threads designated as daemon threads are automatically terminated when the rest of the application has completed.

System Properties

Although it is possible to read host environment variables from Java, it is discouraged for application configuration. Instead, Java allows any number of system property values to be passed to the application when the VM is started. System properties are simply name-value string pairs that are available to the application through the static System.getProperty() method. You can use these properties as a more structured and portable alternative to command-line arguments and environment variables for providing general configuration information to your application at startup. Each system property is passed to the interpreter on the command line using the -D option followed by name=value. For example:

    % java -Dstreet=sesame -Dscene=alley animals.birds.BigBird

The value of the street property is then accessible this way:

    String street = System.getProperty("street");

An application can get its configuration in myriad other ways, including via files or network configuration at runtime.

The Classpath

The concept of a path should be familiar to anyone who has worked on a DOS or Unix platform. It’s an environment variable that provides an application with a list of places to look for some resource. The most common example is a path for executable programs. In a Unix shell, the PATH environment variable is a colon-separated list of directories that are searched, in order, when the user types the name of a command. The Java CLASSPATH environment variable, similarly, is a list of locations that are searched for Java class files. Both the Java interpreter and the Java compiler use the CLASSPATH when searching for packages and Java classes.

An element of the classpath can be a directory or a JAR file. Java also supports archives in the conventional ZIP format, but JAR and ZIP are really the same format. JARs are simple archives that include extra files (metadata) that describe each archive’s contents. JAR files are created with the JDK’s jar utility; many tools for creating ZIP archives are publicly available and can be used to inspect or create JAR files as well. The archive format enables large groups of classes and their resources to be distributed in a single file; the Java runtime automatically extracts individual class files from the archive as needed.

The precise means and format for setting the classpath vary from system to system. On a Unix system (including Mac OS X), you set the CLASSPATH environment variable with a colon-separated list of directories and class archive files:

    %export CLASSPATH=/home/vicky/Java/classes:/home/josh/lib/foo.jar:.

This example specifies a classpath with three locations: a directory in the user’s home, a JAR file in another user’s directory, and the current directory, which is always specified with a dot (.). The last component of the classpath, the current directory, is useful when you are tinkering with classes.

On a Windows system, the CLASSPATH environment variable is set with a semicolon-separated list of directories and class archive files:

    C:> set CLASSPATH=C:homevickyJavaclasses;C:homejoshlibfoo.jar;.

The Java launcher and the other command-line tools know how to find the core classes, which are the classes included in every Java installation. The classes in the java.lang, java.io, java.net, and javax.swing packages, for example, are all core classes so you do not need to include these classes in your classpath.

The classpath may also include “*” wildcards that match all JAR files within a directory. For example:

export CLASSPATH=/home/pat/libs/*

To find other classes, the Java interpreter searches the elements of the classpath in order. The search combines the path location and the components of the fully qualified class name. For example, consider a search for the class animals.birds.BigBird. Searching the classpath directory /usr/lib/java means the interpreter looks for an individual class file at /usr/lib/java/animals/birds/BigBird.class. Searching a ZIP or JAR archive on the classpath, say /home/vicky/myutils.jar, means that the interpreter looks for component file animals/birds/BigBird.class within that archive.

For the Java runtime, java, and the Java compiler, javac, the classpath can also be specified with the -classpath option:

    % javac -classpath /home/pat/classes:/utils/utils.jar:. Foo.java

If you don’t specify the CLASSPATH environment variable or command-line option, the classpath defaults to the current directory (.); this means that the files in your current directory are normally available. If you change the classpath and don’t include the current directory, these files will no longer be accessible.

We suspect that about 80 percent of the problems that newcomers have when first learning Java are classpath-related. You may wish to pay particular attention to setting and checking the classpath when getting started. If you’re working inside an IDE, it may remove some or all of the burden of managing the classpath. Ultimately, however, understanding the classpath and knowing exactly what is in it when your application runs is very important to your long-term sanity. The javap command, discussed next, can be useful in debugging classpath issues.

javap

A useful tool to know about is the javap command. With javap, you can print a description of a compiled class. You don’t need the source code, and you don’t even need to know exactly where it is, only that it is in your classpath. For example:

    % javap java.util.Stack

prints the information about the java.util.Stack class:

Compiled from "Stack.java"
public class java.util.Stack<E> extends java.util.Vector<E> {
  public java.util.Stack();
  public E push(E);
  public synchronized E pop();
  public synchronized E peek();
  public boolean empty();
  public synchronized int search(java.lang.Object);
}

This is very useful if you don’t have other documentation handy and can also be helpful in debugging classpath problems. Using javap, you can determine whether a class is in the classpath and possibly even which version you are looking at (many classpath issues involve duplicate classes in the classpath). If you are really curious, you can try javap with the -c option, which causes it to also print the JVM instructions for each method in the class!

Modules

As of Java 9, an alternative to the classic classpath approach (which remains available), you can take advantage of the new modules approach to Java applications. Modules allow for more fine-grained, performant application deployments—even when the application in question is large. They require extra setup so we won’t be tackling them in this book, but it is important to know that any commercially distributed app will likely be module-based. You can check out Java 9 Modularity by Paul Bakker and Sander Mak for more details and help modularizing your own larger projects if you start looking to share your work beyond just posting source to public repositories.

The Java Compiler

In this section, we’ll say a few words about javac, the Java compiler in the JDK. The javac compiler is written entirely in Java, so it’s available for any platform that supports the Java runtime system. javac turns Java source code into a compiled class that contains Java bytecode. By convention, source files are named with a .java extension; the resulting class files have a .class extension. Each source code file is considered a single compilation unit. As you’ll see in Chapter 5, classes in a given compilation unit share certain features, such as package and import statements.

javac allows one public class per file and insists that 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 and is named for the file. Avoid packing too many classes into a single source file. Packing classes together in a .java file only superficially associates them. In Chapter 5, we’ll talk about inner classes, classes that contain other classes and interfaces.

As an example, place the following source code in the file BigBird.java:

    package animals.birds;

    public class BigBird extends Bird {
        ...
    }

Next, compile it with:

    % javac BigBird.java

Unlike the Java interpreter, which takes just a class name as its argument, javac needs a filename (with the .java extension) to process. The previous command produces the class file BigBird.class in the same directory as the source file. While it’s nice to see the class file in the same directory as the source for this example, for most real applications, you need to store the class file in an appropriate place in the classpath.

You can use the -d option with javac to specify an alternative directory for storing the class files javac 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 the other classes your class references as long as they are in the classpath in either source or compiled form. During compilation, Java resolves all other class references using the classpath.

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 classes and recompiles them as necessary. A compiled Java class remembers the source file from which it was compiled, and as long as the source file is available, javac can recompile it 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. For that and many other reasons, most projects use a real build utility such as Gradle to manage builds, packaging, and more.

Finally, it’s important to note that javac can compile an application even if only the compiled (binary) versions of some of the classes are available. You don’t need source code for all 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 typesafe (and exception safe) as compiling with Java source code.

Trying Java

Java 9 introduced a utility call jshell which allows you to try out bits of Java code and see the results immediately. jshell is a REPL—a Read Evaluate Print Loop. Many languages have them and prior to Java 9 there were many third-party variations available, but nothing built into the JDK itself. We saw a hint of what jshell can do in the previous chapter; let’s look a little more carefully at its capabilities.

You can use a terminal or command window from your operating system, or you can open a terminal tab in IntelliJ IDEA as shown in Figure 3-2. Just type jshell at your command prompt and you’ll see a bit of version information along with a quick reminder about how to view help from within the REPL.

lj5e 0302
Figure 3-2. Starting jshell inside IDEA

Let’s go ahead and try that help command now:

|  Welcome to JShell -- Version 12
|  For an introduction type: /help intro

jshell> /help intro
|
|                                   intro
|                                   =====
|
|  The jshell tool allows you to execute Java code, getting immediate results.
|  You can enter a Java definition (variable, method, class, etc), like:  int x = 8
|  or a Java expression, like:  x + x
|  or a Java statement or import.
|  These little chunks of Java code are called 'snippets'.
|
|  There are also the jshell tool commands that allow you to understand and
|  control what you are doing, like:  /list
|
|  For a list of commands: /help

jshell is quite powerful and we won’t be using all of its features in this book. However, we will certainly be using its ability to try Java code and make quick tweaks and try again throughout most of the remaining chapters. Think back to our HelloJava2 example “HelloJava2: The Sequel”. We can create user interface elements like that JFrame right in the REPL and then manipulate them—all while getting immediate feedback! No need to save, compile, run, edit, save, compile, run, etc. Let’s try:

jshell> JFrame frame = new JFrame( "HelloJava2" )
|  Error:
|  cannot find symbol
|    symbol:   class JFrame
|  JFrame frame = new JFrame( "HelloJava2" );
|  ^----^
|  Error:
|  cannot find symbol
|    symbol:   class JFrame
|  JFrame frame = new JFrame( "HelloJava2" );
|                     ^----^

Oops! jshell is smart and feature rich, but it is also quite literal. Remember that if you want to use a class not included in the default package, you have to import it. That’s true in Java source files, and it’s true when using jshell. Let’ try again:

jshell> import javax.swing.*

jshell> JFrame frame = new JFrame( "HelloJava2" )
frame ==> javax.swing.JFrame[frame0,0,23,0x0,invalid,hidden ... led=true]

That’s better. A little strange, probably, but better. Our frame object has been created. That extra information after the ==> arrow is just the details about our JFrame such as its size (0x0) and position on screen (0,23). Other types of objects will show other details. Let’s give our frame some width and height like we did before and get our frame on the screen where we can see it.

jshell> frame.setSize(300,200)

jshell> frame.setLocation(400,400)

jshell> frame.setVisible(true)

You should see a window pop up right before your very eyes! It will be resplendent in modern finery as shown in Figure 3-3.

lj5e 0303
Figure 3-3. Showing a JFrame from jshell

By the way, don’t worry about making mistakes in the REPL. You’ll see an error message but you can just correct whatever was wrong and keep going. As a quick example, imagine making a typo when trying to change the size of the frame:

jshell> frame.setsize(300,300)
|  Error:
|  cannot find symbol
|    symbol:   method setsize(int,int)
|  frame.setsize(300,300)
|  ^-----------^

Java is case-sensitive so setSize() is not the same as setsize(). jshell gives you the same kind of error information that the Java compiler would, but presents it inline. Correct that mistake and watch the frame get a little bigger!

lj5e 0304
Figure 3-4. Changing the size of our frame

Amazing! Well, alright, perhaps it is less than useful, but we’re just starting. Let’s add some text using the JLabel class:

jshell> JLabel label = new JLabel("Hi jshell!")
label ==> javax.swing.JLabel[,0,0,0x0,invalid,alignmentX=0. ... rticalTextPosition=CENTER]

jshell> frame.add(label)
$8 ==> javax.swing.JLabel[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.0,border=,flags=8388608,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,horizontalAlignment=LEADING,horizontalTextPosition=TRAILING,iconTextGap=4,labelFor=,text=Hi jshell!,verticalAlignment=CENTER,verticalTextPosition=CENTER]

Neat, but why didn’t our label show up in the frame? We’ll go into much more detail on this in the chapter on user interfaces, but Java allows some graphical changes to build up before realizing them on your screen. This can be an immensely efficient trick but it can sometimes catch you off guard. Let’s force the frame to redraw itself:

jshell> frame.revalidate()

jshell> frame.repaint()
lj5e 0305
Figure 3-5. Adding a JLabel to our frame

Now we can see our label. Some actions will automatically trigger a call to revalidate() or repaint(). Any component already added to our frame before we make it visible, for example, would appear right away when we do show the frame. Or we can remove the label similar to how we added it. Watch again to see what happens when we change the size of the frame immediately after removing our label.

jshell> frame.remove(label) // as with add(), things don't change immediately

jshell> frame.setSize(400,150)
lj5e 0306
Figure 3-6. Removing a label and resizing our frame

See? We have a new, slimmer window and no label—all without forced repainting. We’ll do more work with UI elements in later chapters, but let’s try one more tweak to our label just to show you how easy it is to try out new ideas or methods you looked up in the documenatation. We can center the label’s text, for example, resulting in something like Figure 3-7.

jshell> frame.add(label)
$45 ==> javax.swing.JLabel[,0,0,300x278,invalid,alignmentX=0.0,alignmentY=0.0,border=,flags=8388608,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,horizontalAlignment=LEADING,horizontalTextPosition=TRAILING,iconTextGap=4,labelFor=,text=Hi jshell!,verticalAlignment=CENTER,verticalTextPosition=CENTER]

jshell> frame.revalidate()

jshell> frame.repaint()

jshell> label.setHorizontalAlignment(JLabel.CENTER)
lj5e 0307
Figure 3-7. Centering the text on our label

We know this was another whirlwind tour with several bits of code that might not make sense yet, like why is CENTER all caps? or why is the class name JLabel used before our center alignment? Hopefully typing along, probably making a few small mistakes, correcting them, and seeing the results makes you want to know more. We just want to make sure you have the tools needed to continue playing along as you go through the rest of this book. Like so many other skills, programming benefits from doing in addition to reading!

JAR Files

Java Archive (JAR) files are Java’s suitcases. They are the standard and portable way to pack up all the parts of your Java application into a compact bundle for distribution or installation. You can put whatever you want into a JAR file: Java class files, serialized objects, data files, images, audio, etc. A JAR file can also carry one or more digital signatures that attest to its integrity and authenticity. A signature can be attached to the file as a whole or to individual items in the file.

The Java runtime system can load class files directly from an archive in your CLASSPATH, as described earlier. Nonclass files (data, images, etc.) contained in your JAR file can also be retrieved from the classpath by your application using the getResource() method. Using this facility, your code doesn’t have to know whether any resource is in a plain file or a member of a JAR archive. Whether a given class or data file is an item in a JAR file or an individual file on the classpath, you can always refer to it in a standard way and let Java’s class loader resolve the location.

File Compression

Items stored in JAR files are compressed with the standard ZIP file compression. Compression makes downloading classes over a network much faster. A quick survey of the standard Java distribution shows that a typical class file shrinks by about 40 percent when it is compressed. Text files such as HTML or ASCII containing English words often compress to one-tenth their original size or less. (On the other hand, image files don’t normally get smaller when compressed as most common image formats are themselves a compression format.)

Java also has an archive format called Pack200, which is optimized specifically for Java class bytecode and can achieve over four times greater compression of Java classes than ZIP alone. We’ll talk about Pack200 later in this chapter.

The jar Utility

The jar utility provided with the JDK is a simple tool for creating and reading JAR files. Its user interface isn’t particularly friendly. It mimics the Unix tar (tape archive) command. If you’re familiar with tar, you’ll recognize the following incantations:

jar -cvf jarFile path [ path ] [ … ]

Create jarFile containing path(s).

jar -tvf jarFile [ path ] [ … ]

List the contents of jarFile, optionally showing just path(s).

jar -xvf jarFile [ path ] [ … ]

Extract the contents of jarFile, optionally extracting just path(s).

In these commands, the flag letters c, t, and x tell jar whether it is creating an archive, listing an archive’s contents, or extracting files from an archive. The f means that the next argument is the name of the JAR file on which to operate. The optional v flag tells jar to be verbose when displaying information about files. In verbose mode, you get information about file sizes, modification times, and compression ratios.

Subsequent items on the command line (i.e., anything aside from the letters telling jar what to do and the file on which jar should operate) are taken as names of archive items. If you’re creating an archive, the files and directories you list are placed in it. If you’re extracting, only the filenames you list are extracted from the archive. (If you don’t list any files, jar extracts everything in the archive.)

For example, let’s say we have just completed our new game, spaceblaster. All the files associated with the game are in three directories. The Java classes themselves are in the spaceblaster/game directory, spaceblaster/images contains the game’s images, and spaceblaster/docs contains associated game data. We can pack all this in an archive with this command:

    % jar -cvf spaceblaster.jar spaceblaster

Because we requested verbose output, jar tells us what it is doing:

    adding:spaceblaster/ (in=0) (out=0) (stored 0%)
    adding:spaceblaster/game/ (in=0) (out=0) (stored 0%)
    adding:spaceblaster/game/Game.class (in=8035) (out=3936) (deflated 51%)
    adding:spaceblaster/game/Planetoid.class (in=6254) (out=3288) (deflated 47%)
    adding:spaceblaster/game/SpaceShip.class (in=2295) (out=1280) (deflated 44%)
    adding:spaceblaster/images/ (in=0) (out=0) (stored 0%)
    adding:spaceblaster/images/spaceship.gif (in=6174) (out=5936) (deflated 3%)
    adding:spaceblaster/images/planetoid.gif (in=23444) (out=23454) (deflated 0%)
    adding:spaceblaster/docs/ (in=0) (out=0) (stored 0%)
    adding:spaceblaster/docs/help1.html (in=3592) (out=1545) (deflated 56%)
    adding:spaceblaster/docs/help2.html (in=3148) (out=1535) (deflated 51%)

jar creates the file spaceblaster.jar and adds the directory spaceblaster, adding the directories and files within spaceblaster to the archive. In verbose mode, jar reports the savings gained by compressing the files in the archive.

We can unpack the archive with this command:

    % jar -xvf spaceblaster.jar

Likewise, we can extract an individual file or directory with:

    % jar -xvf spaceblaster.jar filename

But, of course, you normally don’t have to unpack a JAR file to use its contents; Java tools know how to extract files from archives automatically. We can list the contents of our JAR with the command:

    % jar -tvf spaceblaster.jar

Here’s the output; it lists all the files, their sizes, and their creation times:

    0 Thu May 15 12:18:54 PDT 2003 META-INF/
      1074 Thu May 15 12:18:54 PDT 2003 META-INF/MANIFEST.MF
         0 Thu May 15 12:09:24 PDT 2003 spaceblaster/
         0 Thu May 15 11:59:32 PDT 2003 spaceblaster/game/
      8035 Thu May 15 12:14:08 PDT 2003 spaceblaster/game/Game.class
      6254 Thu May 15 12:15:18 PDT 2003 spaceblaster/game/Planetoid.class
      2295 Thu May 15 12:15:26 PDT 2003 spaceblaster/game/SpaceShip.class
         0 Thu May 15 12:17:00 PDT 2003 spaceblaster/images/
      6174 Thu May 15 12:16:54 PDT 2003 spaceblaster/images/spaceship.gif
     23444 Thu May 15 12:16:58 PDT 2003 spaceblaster/images/planetoid.gif
         0 Thu May 15 12:10:02 PDT 2003 spaceblaster/docs/
      3592 Thu May 15 12:10:16 PDT 2003 spaceblaster/docs/help1.html
      3148 Thu May 15 12:10:02 PDT 2003 spaceblaster/docs/help2.html

JAR manifests

Note that the jar command automatically adds a directory called META-INF to our archive. The META-INF directory holds files describing the contents of the JAR file. It always contains at least one file: MANIFEST.MF. The MANIFEST.MF file can contain a “packing list” naming the files in the archive along with a user-definable set of attributes for each entry.

The manifest is a text file containing a set of lines in the form keyword: value. The manifest is, by default, empty and contains only JAR file version information:

Manifest-Version: 1.0
Created-By: 1.7.0_07 (Oracle Corporation)

It is also possible to sign JAR files with a digital signature. When you do this, digest (checksum) information is added to the manifest for each archived item (as shown next) and the META-INF directory holds digital signature files for items in the archive.

    Name: com/oreilly/Test.class
    SHA1-Digest: dF2GZt8G11dXY2p4olzzIc5RjP3=
    ...

You can add your own information to the manifest descriptions by specifying your own supplemental, manifest file when you create the archive. This is one possible place to store other simple kinds of attribute information about the files in the archive, perhaps version or authorship information.

For example, we can create a file with the following keyword: value lines:

    Name: spaceblaster/images/planetoid.gif
    RevisionNumber: 42.7
    Artist-Temperament: moody

To add this information to the manifest in our archive, place it in a file called myManifest.mf and give the following jar command:

    % jar -cvmf myManifest.mf spaceblaster.jar spaceblaster

We included an additional option, m, which specifies that jar should read additional manifest information from the file given on the command line. How does jar know which file is which? Because m is before f, it expects to find the manifest information before the name of the JAR file it will create. If you think that’s awkward, you’re right; get the names in the wrong order, and jar does the wrong thing.

An application can get this manifest information from a JAR file using the java.util.jar.Manifest class.

Making a JAR file runnable

Aside from attributes, you can put a few special values in the manifest file. One of these, Main-Class, allows you to specify the class containing the primary main() method for an application contained in the JAR:

    Main-Class: com.oreilly.Game

If you add this to your JAR file manifest (using the m option described earlier), you can run the application directly from the JAR:

    % java -jar spaceblaster.jar

Some GUI environments used to support double-clicking on the JAR file to launch the application. The interpreter looks for the Main-Class value in the manifest, then loads the designated class as the application’s startup class. This feature seems to be in flux and is not supported on all operatings systems so you may need to investigate other Java application distribution options if you are creating an app you want to share with others.

The pack200 Utility

Pack200 is an archive format that is optimized for storing compiled Java class files. Pack200 is not a new form of compression, but rather an efficient layout for class information that eliminates many types of waste and redundancy across related classes. It is effectively a bulk class-file format that deconstructs many classes and reassembles their parts efficiently into one catalog. This then allows a standard compression format like ZIP to work at maximum efficiency on the archive, achieving four or more times greater compression. The Java runtime does not understand the Pack200 format, so you cannot place archives of this type into the classpath. Instead, it is mainly an intermediate format that is very useful for transferring application JARs over the network for applets or other kinds of web-based applications.

It was popular for delivering applets around the web back in the day, but as applets have faded (well, disappeared), so too has the utility of the Pack200 format. You may still encounter some .pack.gz files so we wanted to mention the tools you would use, but the tools themselves have been removed in Java 14.

You can convert a JAR to and from Pack200 format with the pack200 and unpack200 commands supplied with the JDK and OpenJDK prior to Java version 14.

For example, to convert foo.jar to foo.pack.gz, use the pack200 command:

    % pack200 foo.pack.gz foo.jar

To convert foo.pack.gz to foo.jar:

    % unpack200 foo.pack.gz foo.jar

Note that the Pack200 process completely tears down and reconstructs your classes at the class level, so the resulting foo.jar file will not be byte-for-byte the same as the original.

Building up

Alrighty then. There are obviously quite a few tools in the Java ecosystem—they got the name right with the initial bundling of everything into the Java Development “Kit”. You won’t use every tool mentioned above right away so don’t worry if the list of utilities seems a little overwhelming. We will focus on using the javac compiler utility as you wade farther and farther into the Java waters. Even then, the compiler and several other tools are helpfully wrapped up behind buttons in your IDE. Our goal for this chapter is to make sure you know what tools are out there so that you can come back for details when you need them.

Hopefully now that you’ve seen some of the arsenal available to help process and package Java code, you’re ready to write some of that code. The next several chapters lay the foundations for doing just that, so let’s dive in!

1 You can search for “OpenJDK provider” to find a current list of options as well as some useful comparisons between providers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.12.242