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.
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:
| Thing | What it is |
|---|---|
| The language | the syntax you write: class, if, generics |
| The platform | the language plus the standard library plus the JVM specification |
| A JDK | somebody's build of the tools that implement the platform |
| The JVM | the 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:
| Year | What happened | What you still meet |
|---|---|---|
| 1996 | JDK 1.0 | the oldest parts of the library, Vector, Hashtable and Date, still present and never to be removed |
| 2004 | Java 5 | generics, enums, annotations, autoboxing: the start of Java as it is written today |
| 2006–07 | Sun releases Java as open source, as OpenJDK | why every JDK you can download is a build of the same source |
| 2010 | Oracle acquires Sun | why Oracle owns the trademark, and the licensing history in the next lesson |
| 2014 | Java 8 | lambdas, streams, java.time, and still the floor in many companies |
| 2017 | Java 9 and its module system; Java EE handed to the Eclipse Foundation | module-info.java, and javax imports becoming jakarta |
| 2018 on | a release every six months | the 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.
| Claim | What it meant | How it holds up |
|---|---|---|
| Simple | no pointer arithmetic, no manual freeing of memory, no multiple inheritance of classes, no operator overloading | simpler than C++. The language is still small; the ecosystem around it is not |
| Object-oriented | every piece of code lives in a class | not purely: the eight primitives are not objects, and since Java 8 the language is partly functional too |
| Robust | types checked at compile time, array bounds checked at run time, memory reclaimed by a garbage collector | true, and the most valuable word on the list. A bad index throws an exception instead of quietly overwriting some other data |
| Secure | bytecode is verified before it runs, and code cannot reach memory it was not given | the 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, portable | compiled to bytecode rather than to one processor; an int is 32 bits on every machine | true, with leaks. The next section |
| Interpreted, high-performance | two words that contradicted each other, and early Java was genuinely slow | resolved by the JIT compiler, which turns frequently run code into machine code while the program runs |
| Multithreaded | threads and synchronized in the language from 1.0 | true, and virtual threads in Java 21 made threads cheap |
| Distributed | networking in the standard library | mostly historical: it meant sockets and remote method calls. Today it means an HTTP client |
| Dynamic | classes are loaded when first needed, and can be inspected while the program runs | true, 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:
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 x64javac 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 withPath.of("data", "orders.csv")rather than by joining strings. - Line endings.
\r\non Windows,\nelsewhere.System.lineSeparator(), or%nin 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 namedconfig.jsonwhen the program runs from a directory on a Mac, and returnsnullon 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:
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 readySo 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.8and8are 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.
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");
}
}javac Where.java, thenjava Where. What does it report?- 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? - Make it run in that container without changing the code or the image. Then run it once more with
--platform linux/amd64added after--rm.
No Docker? Read the answers anyway; the error in step 2 is the part worth recognising.
Answers
- Your own machine, something like
Mac OS X aarch64, Java 25.0.1, an int is 32 bits. - 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. - Recompile with
javac --release 21 Where.java, which emits a class file a Java 21 JVM accepts. The container now printsLinux aarch64, Java 21.0.12, an int is 32 bitson an ARM machine, and with--platform linux/amd64it printsLinux amd64. One class file, three combinations of operating system and processor, three differentjavaprograms running it, and anintof 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
staticmethod belongs to no object at all.