Java, and which Java

Where the language came from, what its original promises still mean, why one class file runs on any machine, what SE and Jakarta EE are, and why the version numbers jump around.

12 min read🔧 Setting Up Java

Java is thirty years old, which is why some of it looks the way it does and why there are so many things called Java. This lesson is the map: where it came from, what it promised, why one compiled file runs on any machine, and which of the things called Java you actually want.

What "Java" refers to

Four different things wear the name, and conflating them is the source of most confusion:

ThingWhat it is
The languagethe syntax you write: class, if, generics
The platformthe language plus the standard library plus the JVM specification
A JDKsomebody's build of the tools that implement the platform
The JVMthe program that runs compiled code

When someone says "we're on Java 21", they mean a platform version — which implies a language version, a library version, and a JVM that understands the bytecode that version emits.

Where it came from

In 1991 a small team at Sun Microsystems, led by James Gosling, began a language for consumer devices: set-top boxes and the like, where the chip inside might change from one product to the next. That constraint is the origin of the idea that matters most. Instead of compiling for one processor, compile to something no processor runs, and put a small program on each device that runs it. The language was called Oak; the name was already taken as a trademark, and it shipped in 1995 as Java.

The device market did not arrive. The web did. Applets, small Java programs running inside a web page, made the language famous, and "write once, run anywhere" became the slogan. Applets are long gone. The portability they advertised turned out to matter far more on servers, where nobody wants to recompile for each kind of machine in the rack.

A handful of dates are worth knowing, because each left something you will still meet:

YearWhat happenedWhat you still meet
1996JDK 1.0the oldest parts of the library, Vector, Hashtable and Date, still present and never to be removed
2004Java 5generics, enums, annotations, autoboxing: the start of Java as it is written today
2006–07Sun releases Java as open source, as OpenJDKwhy every JDK you can download is a build of the same source
2010Oracle acquires Sunwhy Oracle owns the trademark, and the licensing history in the next lesson
2014Java 8lambdas, streams, java.time, and still the floor in many companies
2017Java 9 and its module system; Java EE handed to the Eclipse Foundationmodule-info.java, and javax imports becoming jakarta
2018 ona release every six monthsthe version numbers below

The promises, read thirty years later

Sun described Java in eleven words: simple, object-oriented, distributed, interpreted, robust, secure, architecture-neutral, portable, high-performance, multithreaded and dynamic. That list is still how "the features of Java" is taught and asked about, and every word on it was a comparison with C and C++ in the mid-1990s. Read that way, they make sense. Read as absolute claims, several are wrong.

ClaimWhat it meantHow it holds up
Simpleno pointer arithmetic, no manual freeing of memory, no multiple inheritance of classes, no operator overloadingsimpler than C++. The language is still small; the ecosystem around it is not
Object-orientedevery piece of code lives in a classnot purely: the eight primitives are not objects, and since Java 8 the language is partly functional too
Robusttypes checked at compile time, array bounds checked at run time, memory reclaimed by a garbage collectortrue, and the most valuable word on the list. A bad index throws an exception instead of quietly overwriting some other data
Securebytecode is verified before it runs, and code cannot reach memory it was not giventhe memory-safety half holds. The sandbox half, the Security Manager built for applets, was deprecated in Java 17 and disabled in Java 24
Architecture-neutral, portablecompiled to bytecode rather than to one processor; an int is 32 bits on every machinetrue, with leaks. The next section
Interpreted, high-performancetwo words that contradicted each other, and early Java was genuinely slowresolved by the JIT compiler, which turns frequently run code into machine code while the program runs
Multithreadedthreads and synchronized in the language from 1.0true, and virtual threads in Java 21 made threads cheap
Distributednetworking in the standard librarymostly historical: it meant sockets and remote method calls. Today it means an HTTP client
Dynamicclasses are loaded when first needed, and can be inspected while the program runstrue, and it is what Spring is built on: reading classes it had never heard of when it was compiled

The two reasons companies choose Java today are not on the list at all. Code written twenty years ago still compiles and runs, and nearly anything a backend needs, from a database driver to a message-broker client, already exists as a mature library.

Why one class file runs everywhere

A C compiler produces machine code for one processor and one operating system. A binary built on a Linux x86 server will not start on an ARM Mac, so a C program is compiled once per target. Java moved that step:

plaintext
C       hello.c ─ compiler for Linux x64 ─▶ runs on Linux x64 only
        hello.c ─ compiler for macOS ARM ─▶ runs on macOS ARM only
 
Java    Hello.java ─ javac, anywhere ─▶ Hello.class ─▶ JVM for Linux x64
                                                   ─▶ JVM for macOS ARM
                                                   ─▶ JVM for Windows x64

javac produces bytecode: instructions for a machine that does not physically exist, in a file format that pins down everything that differs between real machines, such as how many bits an int has and in what order a number's bytes are stored. The part that is specific to a machine is the JVM. That is the trick, and it is worth saying precisely:

Java programs are platform independent. The JVM is not. That is why the JDK download page asks for your operating system and processor, and why the java command you install is a native macOS, Linux or Windows program. Somebody still compiles for every platform. It is the people who build the JDK, once per release, rather than you, for every release of your application.

A container image is the same story one level down: the jar inside it is portable, and the JVM inside it was built for amd64 or arm64.

Where it leaks

The bytecode is portable. The machine your program talks to is not:

  • Paths. Windows separates directories with \, everything else with /. Build a path with Path.of("data", "orders.csv") rather than by joining strings.
  • Line endings. \r\n on Windows, \n elsewhere. System.lineSeparator(), or %n in a format string, gives you the local one.
  • File-name case. A Mac's disk ignores case by default and a Linux server's does not. getResource("/Config.json") finds a file named config.json when the program runs from a directory on a Mac, and returns null on the Linux server — or inside a jar, even on the Mac.
  • Default text encoding. Before Java 18 it came from the operating system, so a file read without naming an encoding could decode differently on Windows. Since Java 18 the default is UTF-8 everywhere. Name the encoding anyway.
  • Native code. A library that ships compiled C for speed, such as a compression codec or an embedded database, needs a separate build for every platform and fails on one it lacks.
  • Versions. Portability runs across machines, not backwards in time. A class file compiled by JDK 25 does not load on a Java 21 JVM.

The editions, which are fewer than you think

Java SE (Standard Edition) is Java. The language, the core library, the JVM. When this course says Java, it means SE.

Jakarta EE (formerly Java EE) is a set of additional specifications for server-side applications — servlets, persistence, dependency injection contracts. It is not a different Java; it is libraries and rules layered on SE. The rename happened because Oracle kept the "Java" trademark when the work moved to the Eclipse Foundation, which is why you will see both names in the same codebase and they mean the same lineage.

You will meet Jakarta as import statements — jakarta.persistence.Entity — long before you meet it as a concept. Spring uses parts of it and ignores the rest.

Java ME was for constrained devices and is essentially historical. Android uses the Java language with a different runtime and a different library, which is why Android answers on Stack Overflow are often wrong for a backend.

Why the version numbers look like that

The numbering is worth ninety seconds, because it explains artefacts you will see.

Versions ran 1.0, 1.1, 1.2 … and the marketing name diverged from the technical one: Java 5 is 1.5, Java 8 is 1.8. That is why java -version on an old machine prints 1.8.0_402 and means Java 8, and why Maven configuration still says <source>1.8</source>.

From Java 9 the numbering became plain — 9, 10, 11 — and the release cadence changed to a new version every six months. That is the change that matters:

plaintext
before Java 9    a big release every 2–3 years, features waited for it
after Java 9     a release every six months, features ship when ready

So Java 17, 21 and 25 are not three times as big as Java 8 was. Most releases are small, and the ones people talk about are the long-term support releases. That is the next lesson.

What this means for you

  • Search results about "Java EE" and "Jakarta EE" are usually about the same thing.
  • Code samples for Android may not apply.
  • A tutorial written for Java 8 is mostly still correct — the language is strongly backward compatible, which is both its great virtue and why some of it looks dated.
  • 1.8 and 8 are the same version, and you will see both in the same project.

Try it yourself

One class file, three machines

Save this, compile it once, and run it.

Where.javajava
public class Where {
    public static void main(String[] args) {
        System.out.println(System.getProperty("os.name") + " " + System.getProperty("os.arch")
            + ", Java " + System.getProperty("java.version")
            + ", an int is " + Integer.SIZE + " bits");
    }
}
  1. javac Where.java, then java Where. What does it report?
  2. If Docker is installed, run the same class file on Linux without recompiling: docker run --rm -v "$PWD":/app -w /app eclipse-temurin:21-jre java Where. If your JDK is newer than 21, what happens?
  3. Make it run in that container without changing the code or the image. Then run it once more with --platform linux/amd64 added after --rm.

No Docker? Read the answers anyway; the error in step 2 is the part worth recognising.

Answers
  1. Your own machine, something like Mac OS X aarch64, Java 25.0.1, an int is 32 bits.
  2. On a newer JDK it fails before your code runs: UnsupportedClassVersionError: Where has been compiled by a more recent version of the Java Runtime (class file version 69.0), this version of the Java Runtime only recognizes class file versions up to 65.0. The file is portable across machines, not versions.
  3. Recompile with javac --release 21 Where.java, which emits a class file a Java 21 JVM accepts. The container now prints Linux aarch64, Java 21.0.12, an int is 32 bits on an ARM machine, and with --platform linux/amd64 it prints Linux amd64. One class file, three combinations of operating system and processor, three different java programs running it, and an int of 32 bits in every one.

Misconceptions

  • "Java EE is a different language." It is a set of specifications for libraries that run on ordinary Java SE.
  • "Java 8 is obsolete." It is still the most deployed version in many organisations, and this course teaches from it deliberately for that reason.
  • "A new version every six months means constant upgrades." Most teams move between LTS releases, which is roughly every two years — and even that is often late.
  • "Java is platform independent, so the JDK is too." The JDK is a native program built separately for each operating system and processor. Only what it runs is portable.
  • "Write once, run anywhere means no platform bugs." Paths, line endings, file-name case, text encodings and native libraries all differ, and each has broken a program that passed every test on a laptop.
  • "Java is purely object-oriented." The eight primitives are not objects, and a static method belongs to no object at all.
Progress is saved on this device and to your account when signed in.