Java is generally thought of in terms of three platforms: Standard Edition (SE), Enterprise Edition (EE), and Micro Edition (ME). Each describes the combination of a language version, a set of standard libraries, and a virtual machine (see below) to execute the code. EE is a superset of SE--any EE application can assume the existence of all of the SE libraries--and EE's use of the language is identical to SE's.
Because of the limitations of small devices like phones and set-top boxes, Java Micro Edition differs significantly from its siblings. It is not a subset of SE (as SE is of EE), as some of its libraries exist only in Micro Edition. Moreover, ME eliminates some language features, such as the
float primitive and
Float class, reflecting the computing limitations of the platforms it runs on. Requiring different tools than SE and EE, and with profound differences in devices that makes code portability far less realistic in the micro space, many Java developers see ME as utterly alien.
At some point, Java source needs to become platform-native executable code. This typically requires a two-step process: the developer compiles his or her source into Java bytecode, and then a Java Virtual Machine (JVM) converts this into native code for the host platform. This latter step originally was performed by interpretation--taking each JVM instruction and converting it on the fly to one or more native instructions. Later, just-in-time (JIT) compilers converted all of a Java program from JVM bytecode to native code as the program started up. In the modern era, there are multiple approaches. Sun's HotSpot compiler starts by interpreting code and profiling it at runtime, compiling and optimizing those parts that are found to be most critical to the program's operation. The "mixed mode interpreter" of IBM's JVMs works much the same way. These approaches avoid the startup performance hit entailed by JITing the entire program, but means that performance arrives over time, as critical code sections are located and optimized. Long-running server processes are well-served by this approach, client applications less so.
As is the case with primitives, the two-step compile cycle of Java now starts to look like a premature optimization to some critics. If you're going to wait until runtime to compile from Java bytecode to native, they ask, why not save the developer a step by interpreting not Java bytecode, but Java source? As Tate notes in Beyond Java, "Java is not the simplest of languages. Nor is it friendly to very short iterations ... Other languages let you move from one change to the next without a cumbersome compile/deploy cycle."
Indeed, one of Tate's key criteria in finding potential successors to Java's success is the idea that "the next commercially successful language should have a version that runs in the JVM. That would help a language overcome many obstacles, both political and technical." He points out that a VM approach gives you security ("if you can secure the virtual machine, it's much easier to secure the language"), portability, interoperability, and extensibility. With the JVM having effectively solved these problems, a new language wouldn't need its own VM if it can simply run in the JVM that is already on millions of computers.
But it's also possible to bypass the Java language altogether and go straight to the JVM level. There are already C-to-JVM bytecode compilers, such as the commercial Axiomatic Multi-Platform C, which provides a subset of ANSI C. Furthermore, the growth of Java bytecode manipulation with tools such as ASM and Apache BCEL allow Java applications to create executable classes at runtime. This is no longer Java, but effectively a form of assembly language programming for the JVM.
Perhaps appreciating the desire to run non-Java code on the JVM, a new JSR, "Supporting Dynamically Typed Languages on the JavaTM Platform" (JSR 292), has recently been introduced, specifying a new bytecode that would make the JVM better suited to running languages without static type information.
Java Without the JVM
You can also turn the tables the other way, and run Java without a JVM. After all, at some point, Java source becomes bytecode, which in turn becomes native code--and nobody said you can't do it all at once. The GNU Compiler for Java (GCJ) allows for a one-time, up-front compilation of Java source into an executable for a single platform. While incomplete--it does not support the Abstract Windowing Toolkit (AWT), thus making it unsuitable for AWT or Swing GUI programming--there's enough there to compile server-side and command-line applications.
This process has one obvious downside: cross-platform code becomes bound to a single platform in one step. Moreover, the static compilation is not an automatic trump of HotSpot's dynamic compilation--the author once worked on a project where the performance gain from GCJ was found to be less than five percent beyond the HotSpot version. Still, GCJ can solve important problems, like deploying a runnable Java application without having to worry whether a JVM is available or running a specific version.