Write Once, Run Anywhere

Last modified: Jun 16, 2020
Contents:

One of the main promises that Java offers is “Write Once, Run Anywhere”. The idea is that Java code can be run on almost any platform.

This is stronger than the promise made by (though not always fulfilled by) many programming languages of being portable to many platforms. A C++ programmer, for example, who pays careful attention to language standards might have very good reason to believe that his source code could be copied to a platform with a completely different CPU or operating system, recompiled on that platform, and then executed.

Java, however, offers the promise that your source code can be compiled on one platform, the compiled object code transferred to a platform with a different CPU or operating system, and then executed without requiring recompilation of the source.

We’ll explore that promise in this lab.

1 Compiling the Code

Log in to one of the Dept’s Linux machines. cd to any convenient working directory.

Using a text editor (e.g., emacs or gedit), create a file named HelloWorld.java with the following content (feel free to copy and paste):

/**
* The HelloWorld class implements an application that
* simply prints "Hello World!" to standard output.
*/
class HelloWorld {
   public static void main(String[] args) {
      System.out.println("Hello World!"); // Display the string.
   }
}

We’ll look at this code in detail in a later lesson. But for now, let’s compile and run it.

Java programs get compiled into object code for an imaginary CPU called the “Java Virtual Machine” (JVM). Consequently, you can’t execute compiled Java code directly. You must run a program that simulates a JVM and let that simulated computer execute the Java code.

That may seem a little convoluted, but the JVM simulator is easier to write than a “true” compiler. This means that Java programs can be compiled on one machine and then run on platforms for which no such compiler exists. Also, JVM simulators can be built into other programs (such as web browsers), allowing Java code compiled on one machine to be executed on almost any other machine. By contrast, a true native-code compiler (e.g., g++) produces executables that can only be run on a single kind of computer.

The command to compile Java code is “javac” (“c” for compiler) and the command to execute compiled Java code is “java”. So a typical sequence to compile and execute a single-file Java program like the one you have just produced would be

javac HelloWorld.java
java HelloWorld

Try issuing these two commands now. You should see the expected greeting appear on your screen.

If you now do an

ls

you can see that the javac command created a file named HelloWorld.class. That’s the compiled object code for the imaginary JVM machine. The java command actually launches the emulator for the JVM, tilling it to run the program in the HelloWorld class.

2 Running Elsewhere

Now transfer that HelloWorld.class file to the machine at which you are actually seated. I’ll assume,for the sake of this lab, that you are seated at a Windows PC. (If you are seated at a Linux machine, you might want to find something else to work on for a bit, because otherwise the rest of this exercise won’t be impressive at all.)

A typical Windows machine will already have a JVM emulator, because your web browser would use it whenever it visited a web page with applets. To check this, open up a cmd window (click the Start button and type “cmd” into the “Search programs and files” box for Windows 7 or select “Run…” and then type “cmd” into the “Open:” box for older Windows versions). Within the cmd window, type

java -version

and you should get a version message confirming that java is available.

Now, in that cmd window, cd to the folder into which you transferred the HelloWorld.class file. Give the command

java HelloWorld

and you should see the familiar greeting.

3 A Larger Example

Return to one of the Unix/Linux machines, this time connecting via X or NX.

Copy the file RunAnywhere.java into a convenient directory. If you are using the same directory as earlier, delete any .class files already in that directory.

3.1 Compilation Flags

Like almost any compiler, javac has a number of options that can be given on the command line. Some common ones are:

-cp pathlist
Add the directories and jar files named in the pathlist (multiple items may be separated by ‘:’) to the list of places searched when locating other Java source or object code.
-g
Include debugging information in compiled code (required if you want to be able to run the gdb debugger.
-depend
Check each class used in the code being compiled to see if those mentioned classes’ source code has been changed since they were last compiled. If so, automatically recompile those classes.
-deprecation
Check the code for features that used to be legal in Java, but are expected to become illegal in the near future.
-O
Optimize the compiled code (produces smaller, faster programs but takes longer to compile)

Of these, you would probably use -g almost all the time (unless you are one of those mythical programmers who never needs to debug their code).

So, compile this program with

javac -g RunAnywhere.java

then run the program with

java RunAnywhere

You should see window pop up shortly. Try clicking on the word “run”.

3.2 Execution Options

Like the compiler, the java execution program allows for some command-line options. The most commonly used are:

-cp pathlist
Add the directories and jar files named in the pathlist (multiple items may be separated by ‘:’) to the list of places searched when locating other Java object code.
-jar
Instead of looking for a compiled class file, execute the “default” class in a .jar file.
-Xmx_N_
Sets the amount of memory to be reserved for the heap in the JVM emulator. N is a memory size consisting of an integer followed by a units indicator, usually M for megabytes, e.g., -Xmx512M.

To see the effect of the -cp option, try doing this:

mkdir folder
cd folder
java RunAnywhere
java -cp .. RunAnywhere

The first attempt to execute the program fails because, after we have moved into a different directory, the java program cannot find our compiled class. The -cp option in the second command tells it where to look for that class and so is able to run the program.

Now cd back into your working directory and do an

ls

Notice that you have more than one .class file, even though you compiled only one .java file. That’s a fairly common occurrence.

4 Packaging

Of course, compiling a larger project consisting of several separate .java files is also likely to result in several .class files. It could be awkward if, every time you wanted to transfer a compiled Java program to another platform, you had to transfer an arbitrary number of different .class files. This awkwardness can be relieved by packaging the compiled class files into a single Java Archive (jar).

A jar file is actually a conventional “zip” compressed archive file with a little bit of extra directory information written into a special file (called the manifest) included in the archive.[^

You could create a jar file like this:

jar -cf allInOne.jar *.class 

or

jar -cfe allInOne.jar RunAnywhere *.class 

The -c option indicates that we want to create a new jar archive. The e option in the second form states that we are going to give a default entrypoint (i.e., a default program to execute).[^ The ‘e’ option, as of 8/2015, does not work on OS/X.]

Use the first form if you are simply packing a group of “utility” classes, none of which is a full executable application, or if you are choosing to package together several different programs, none of which is more deserving of a “default” status than any other.

For now, use the second form to create allInOne.jar.

To show that the jar file really is just a variation on a conventional zip archive, try

unzip -l allInOne.jar

to see what’s inside it.

Then delete the .class files and execute the program:

rm *.class
java -cp allInOne.jar RunAnywhere
java -jar allInOne.jar

The first java command uses the -cp option to tell where to look for compiled code (in the jar file) and explicitly names the program (RunAnywhere) that we want to look for. The second simply states that we want to execute the default program in that jar file. Both should work.

Finally, transfer the file allInOne.jar to a Windows PC. You should be able to launch the program from a cmd window by cd’ing to the appropriate directory and giving either of the java commands above. Alternatively, use the Windows GUI to open the folder where you placed that jar and double-click on the jar file to launch its default program.