You can use this guide to get practical information on how to find and install the latest Java, understand the differences between Java distributions (Adoptium, AdoptOpenJDK, Temurin, OpenJDK, OracleJDK etc.), as well as get an overview of Java language features from Java 8-26.
Practical Information
First, let’s have a look at some common, practical questions that people have when trying to choose the right Java version for their project.
TL;DR I only want a download link and know about everything else. Where should I go?
Go to the Adoptium site, choose the very latest release version, download and install it. Then come back to this guide to maybe still learn a thing or two about Java versions.
What is the latest Java version?
As of May 2026, jdk.java.net lists Java 26 as the latest ready-to-use OpenJDK release. Java 27 is available as an early-access / release-candidate build. The current long-term support version (LTS) is Java 25, released in September 2025. Java 21, released in September 2023, is the previous LTS release.
What Java version should I use?
Newer Java versions now follow every 6 months. Hence, Java 21 was released in September 2023, Java 22 in March 2024, Java 23 in September 2024 and so on. In the past, Java release cycles were much longer, up to 3-5 years. This graphic demonstrates that:
With that many new versions coming out, there’s basically these real-world(™) usage scenarios:
-
Legacy projects in companies are often stuck with using Java 8 (see Why are companies still stuck with Java 8?) . Hence you will be forced to use Java 8 as well.
-
Some legacy projects are even stuck on Java 1.5 (released 2004) or 1.6 (released 2006) - sorry, folks, I feel for you!
-
If you are making sure to use the very latest IDEs, frameworks and build tools and starting a greenfield project, you can, without hesitation, use Java 21 (LTS) or Java 25 (LTS).
-
There’s the special field of Android development, where the Java version is basically stuck at Java 7, with a specific set of Java 8 features available. Or you switch to using the Kotlin programming language.
Why are companies still stuck with Java 8?
There’s a mix of different reasons some companies are still stuck with Java 8. To name a few:
-
Build tools (Maven, Gradle etc.) and some libraries initially had bugs with Java versions > 8 and needed updates. For example, certain build tools like Maven would print out "reflective access"-warnings when building Java projects, which simply "felt not ready", even though the builds were fine.
-
Up until Java 8 you were pretty much using Oracle’s JDK builds and you did not have to care about licensing. Oracle changed the licensing scheme In 2019, though, which led the internet go crazy with a ton of articles saying "Java is not free anymore" - and a fair amount of confusion followed. This is however not really an issue, which you’ll learn about in the Java Distributions section of this guide.
-
Some companies have policies to only use LTS versions and rely on their OS vendors to provide them these builds, which takes time.
To sum up: you have a mix of practical issues (upgrading your tools, libraries, frameworks) and political issues.
Why are some Java versions, like 8 also called 1.8?
Java versions before 9 simply had a different naming scheme. So, Java 8 can also be called 1.8, Java 5 can be called 1.5 etc. When you issued the 'java -version' command, with these versions you got output like this:
c:\Program Files\Java\jdk1.8.0_191\bin>java -version
java version "1.8.0_191" (1)
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)
Which simply means Java 8. With the switch to time-based releases with Java 9 the naming scheme also changed, and Java versions aren’t prefixed with 1.x anymore. Now the version number looks like this:
c:\Program Files\Java\jdk11\bin>java -version
openjdk version "11" 2018-09-25 (1)
OpenJDK Runtime Environment 18.9 (build 11+28)
OpenJDK 64-Bit Server VM 18.9 (build 11+28, mixed mode)
What is the difference between the Java versions? Should I learn a specific one?
Coming from other programming languages with major breakages between releases, like say Python 2 to 3, you might be wondering if the same applies to Java.
Java is special in this regard, as it is extremely backwards compatible. This means that your Java 5 or 8 program is very likely to run with a Java 8-26 virtual machine - with a few exceptions you don’t need to worry about for now.
It obviously does not work the other way around, say your program relies on Java 26 features, that are simply not available under a Java 8 JVM.
This means a couple of things:
-
You do not just "learn" a specific Java version, like 12.
-
Rather, you’ll get a good foundation in all language features up until Java 8. This serves as a good base.
-
And then learn, from a guide like this, what additional features came in Java 9-26 and use them whenever you can.
What are examples of these new features between Java versions?
Have a look at the Java Features 8-26 section.
But as a rule of thumb: The older, longer release-cycles (3-5 years, up until Java 8) meant a lot of new features per release.
The 6-month release cycle means a lot less features, per release, so you can catch up quickly on Java 9-26 language features.
What is the difference between a JRE and a JDK?
Up until now, we have only been talking about "Java". But what is Java exactly?
First, you need to differentiate between a JRE (Java Runtime Environment) and a JDK (Java Development Kit).
Historically, you downloaded just a JRE if you were only interested in running Java programs. A JRE includes, among other things, the Java Virtual Machine (JVM) and the "java" command line tool.
To develop new Java programs, you needed to download a JDK. A JDK includes everything the JRE has, as well as the compiler javac and a couple of other tools like javadoc (Java documentation generator) and jdb (Java Debugger).
Now why am I talking in past tense?
Up until Java 8, the Oracle website offered JREs and JDKs as separate downloads - even though the JDK also always included a JRE in a separate folder. With Java 9 that distinction was basically gone, and you are always downloading a JDK. The directory structure of JDKs also changed, with not having an explicit JRE folder anymore.
So, even though some distributions (see Java Distributions section) still offer a separate JRE download, there seems to be the trend of offering just a JDK. Hence, we are going to use Java and JDK interchangeably from now on.
How do I install Java or a JDK then?
Ignore the Java-Docker images, .msi wrappers or platform-specific packages for the moment. In the end, Java is just a .zip file, nothing more, nothing less.
Therefore, all you need to do to install Java onto your machine, is to unzip your jdk-{version}.zip file. You don’t even need administrator rights for that.
Your unzipped Java file will look like this:
Directory C:\dev\jdk-11
12.11.2019 19:24 <DIR> .
12.11.2019 19:24 <DIR> ..
12.11.2019 19:23 <DIR> bin
12.11.2019 19:23 <DIR> conf
12.11.2019 19:24 <DIR> include
12.11.2019 19:24 <DIR> jmods
22.08.2018 19:18 <DIR> legal
12.11.2019 19:24 <DIR> lib
12.11.2019 19:23 1.238 release
The magic happens in the /bin directory, which under Windows looks like this:
Directory C:\dev\jdk-11\bin
...
12.11.2019 19:23 272.736 java.exe
...
12.11.2019 19:23 20.832 javac.exe
...
So all you need to do is unzip that file and put the /bin directory in your PATH variable, so you can call the 'java' command from anywhere.
(In case you are wondering, GUI installers like the one from Oracle or Adoptium will do the unzipping and modifying the PATH variable for you, that’s about it.)
To verify you installed Java correctly, you can then simply run 'java -version'. If the output looks like the one below, you are good to go.
openjdk version "11" 2018-09-25
OpenJDK Runtime Environment 18.9 (build 11+28)
OpenJDK 64-Bit Server VM 18.9 (build 11+28, mixed mode)
Now there’s one question left: Where do you get that Java .zip file from? Which brings us to the topic of distributions.
Java Distributions
There’s a variety of sites offering Java (read: JDK) downloads and it is unclear "who offers what and with which licensing". This section will shed some light on this.
The OpenJDK project
In terms of Java source code (read: the source code for your JRE/JDK), there is only one, living at the OpenJDK project site.
This is just source code however, not a distributable build (think: your .zip file with the compiled java command for your specific operating system). In theory, you and I could produce a build from that source code, call it, say, MarcoJDK and start distributing it. But our distribution would lack certification, to be able to legally call ourselves Java SE compatible.
That’s why in practice, there’s a handful of vendors that actually create these builds, get them certified (see TCK) and then distribute them.
And while vendors cannot, say, remove a method from the String class before producing a new Java build, they can add branding (yay!) or add some other (e.g. CLI) utilities they deem useful. But other than that, the original source code is the same for all Java distributions.
OpenJDK builds (by Oracle) and OracleJDK builds
One of the vendors who builds Java from source is Oracle. This leads to two different Java distributions, which can be very confusing at first.
-
OpenJDK builds by Oracle(!). These builds are free and unbranded, but Oracle won’t release updates for older versions, say Java 15, as soon as Java 16 comes out.
-
OracleJDK, which is a branded, commercial build starting with the license change in 2019. Which means it can be used for free during development, but you need to pay Oracle if using it in production. For this, you get longer support, i.e. updates to versions and a telephone number you can call if your JVM goes crazy. In September 2021, starting with Oracle Java 17, Oracle introduced the No-Fee Terms and Conditions License, making OracleJDK free again, with a couple of limitations you can read about by spending hours on the Oracle website.
Now, historically (pre-Java 8) there were actual source differences between OpenJDK builds and OracleJDK builds, where you could say that OracleJDK was 'better'. But as of today, both versions are essentially the same, with minor differences.
It then boils down to you wanting paid, commercial support (a telephone number) for your installed Java version.
Adoptium’s Eclipse Temurin (formerly AdoptOpenJDK)
In 2017, a group of Java User Group members, developers and vendors (Amazon, Microsoft, Pivotal, Redhat and others) started a community, called AdoptOpenJDK. As of August 2021, the AdoptOpenJDK project moved to a new home and is now called the Eclipse Adoptium project. Here is the current list of Adoptium Working Group Members.
Adoptium provides free, rock-solid OpenJDK builds, called Eclipse Temurin, with longer availability/updates, across a variety of operating systems, architectures and versions.
Highly recommended if you are looking to install Java.
Azul Zulu, Amazon Corretto, SAPMachine
You will find a complete list of OpenJDK builds at the OpenJDK Wikipedia site. Among them are Azul Zulu, Amazon Corretto as well as SapMachine, to name a few. To oversimplify it boils down to you having different support options/maintenance guarantees.
Still, if you’re, for example, working on AWS, it makes sense to just go with their Amazon Corretto OpenJDK builds, provided they offer the Java version you want to use.
A Complete OpenJDK Distribution Overview
Rafael Winterhalter compiled a great list of all available OpenJDK builds, including their OS, architecture, licensing, support and maintenance windows.
Check it out here: https://rafael.codes/openjdk/.
Recommendation
To re-iterate from the beginning, in 2026 and beyond, unless you have very specific requirements, go get your jdk.zip (.tar.gz/.msi/.pkg) file from https://adoptium.net (called Eclipse Temurin) or choose a package provided by your OS-vendor / cloud-provider.
Java Features 8-26
As mentioned at the very beginning of this guide: Essentially all (don’t be picky now) Java 8 language features also work in Java 26. The same goes for all other Java versions in between.
Which in turns means that all language features from Java 8 serve as very good Java base knowledge and everything else (Java 9-26) is pretty much additional features on top of that baseline.
Here’s a quick, deliberately incomplete overview of the features you are most likely to notice as an application developer. I am skipping most VM, GC, crypto, tooling and incubating features unless they change everyday Java code.
- Java 8 -
Java 8 was the big "modern Java" release: lambdas, method references, default methods, the Stream API, Optional, and the new Date and Time API.
List<String> names = Arrays.asList("franz", "ferdinand", "fiel", "vom", "pferd");
names.stream()
.filter(name -> name.startsWith("f"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
If you want a more detailed, thorough overview - including exercises - you can have a look at my Java 8 core features course.
- Java 9 -
Java 9 was mostly about modularizing the JDK itself and introducing the Java Platform Module System ("Jigsaw"). For everyday code, the smaller collection, stream and optional helpers were immediately useful.
List<String> list = List.of("one", "two", "three");
Set<String> set = Set.of("one", "two", "three");
Map<String, String> map = Map.of("foo", "one", "bar", "two");
Stream.iterate("", s -> s + "s")
.takeWhile(s -> s.length() < 10)
.forEach(System.out::println);
Other highlights: JShell, multi-release JARs, private interface methods and the first preview of the new HTTP client.
- Java 10 -
Java 10’s visible developer feature was local-variable type inference: the var keyword. It is still statically typed Java, just with less repetition for local variables.
String explicit = "Marco";
var inferred = "Marco";
- Java 11 -
Java 11 was an LTS release and brought a final, production-ready HTTP client, useful String/Files helpers and the ability to run single-file source programs directly.
"Marco".isBlank();
"Mar\nco".lines();
"Marco ".strip();
Path path = Files.writeString(Files.createTempFile("hello", ".txt"), "Hello");
String text = Files.readString(path);
java MyScript.java
- Java 12 -
Java 12 was a small release for application developers. Its main everyday-language relevance was the first preview of switch expressions, which became standard in Java 14.
- Java 13 -
Java 13 continued switch expressions and introduced text blocks as a preview feature.
String html = """
<html>
<body>Hello</body>
</html>
""";
- Java 14 -
Java 14 standardized switch expressions, previewed records and pattern matching for instanceof, and added helpful NullPointerExceptions.
int length = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
default -> day.toString().length();
};
record Point(int x, int y) { }
if (obj instanceof String s) {
System.out.println(s.toLowerCase());
}
- Java 15 -
Java 15 finalized text blocks and previewed sealed classes. It also removed Nashorn, the old JavaScript engine.
String sql = """
SELECT id, first_name, last_name
FROM users
WHERE active = true
""";
public abstract sealed class Shape
permits Circle, Rectangle, Square {
}
- Java 16 -
Java 16 finalized records and pattern matching for instanceof. It also added Unix-domain socket channels.
record User(long id, String email) { }
if (obj instanceof User user) {
System.out.println(user.email());
}
- Java 17 -
Java 17 was the next LTS release after Java 11. Its biggest application-developer language feature was finalized sealed classes. Pattern matching for switch appeared as a preview.
public sealed interface Payment permits CreditCard, WireTransfer {
}
record CreditCard(String number) implements Payment { }
record WireTransfer(String iban) implements Payment { }
Other notable changes: the Security Manager was deprecated for removal, and the Foreign Function & Memory API continued as an incubating feature.
- Java 18 -
Java 18 made UTF-8 the default charset and added the simple web server tool. Both are small changes, but easy to notice in practice.
jwebserver
- Java 19 -
Java 19 was mostly a preview/incubator release. The interesting direction was clear, though: virtual threads, structured concurrency, record patterns, pattern matching for switch and the Foreign Function & Memory API.
- Java 20 -
Java 20 continued iterating on the same preview/incubator features: scoped values, record patterns, pattern matching for switch, virtual threads, structured concurrency, the Vector API and the Foreign Function & Memory API.
- Java 21 -
Java 21 is an LTS release. For application developers, this is where several long-running preview features became real: virtual threads, record patterns and pattern matching for switch are final. It also added sequenced collections.
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> fetchInvoice(invoiceId));
executor.submit(() -> sendReceipt(email));
}
String label = switch (payment) {
case CreditCard cc -> "card";
case WireTransfer wt -> "bank";
default -> "unknown";
};
SequencedCollection<String> names = new ArrayList<>();
names.addFirst("Marco");
names.addLast("Behler");
Other Java 21 features include generational ZGC, string templates as a preview, unnamed patterns/variables as a preview, scoped values as a preview and structured concurrency as a preview.
- Java 22 -
Java 22 finalized the Foreign Function & Memory API and added a few practical preview features: stream gatherers, statements before super(…), unnamed variables/patterns and multi-file source program launching.
try (Arena arena = Arena.ofConfined()) {
MemorySegment segment = arena.allocate(100);
segment.set(ValueLayout.JAVA_INT, 0, 42);
}
- Java 23 -
Java 23 was again mostly about previews and tooling/API polish. The most visible additions were Markdown documentation comments, module import declarations as a preview, stream gatherers as a second preview and primitive types in patterns/switch as a preview.
/// Finds an active user.
///
/// Returns `Optional.empty()` when the user does not exist.
Optional<User> findUser(long id) {
return repository.findById(id);
}
- Java 24 -
Java 24 finalized stream gatherers and the Class-File API. It also introduced ahead-of-time class loading/linking, permanently disabled the Security Manager, improved virtual-thread synchronization so synchronized blocks do not pin virtual threads, and continued several preview features.
var result = orders.stream()
.gather(Gatherers.windowFixed(3))
.toList();
- Java 25 -
Java 25 is the current LTS release. Its everyday-code highlights are compact source files and instance main methods, finalized module import declarations, finalized flexible constructor bodies and finalized scoped values. Under the hood, Java 25 also adds compact object headers, AOT method profiling, more JFR profiling/tracing work and generational Shenandoah.
void main() {
IO.println("Hello from a compact source file");
}
module import java.base;
void main() {
List<String> names = List.of("Marco", "Java");
IO.println(names);
}
class User {
private final String email;
User(String email) {
if (!email.contains("@")) {
throw new IllegalArgumentException(email);
}
super();
this.email = email;
}
}
Java 25 also keeps primitive types in patterns, instanceof and switch in preview, the Vector API in incubation and PEM encodings/stable values in preview.
- Java 26 -
Java 26 is not an LTS release. Its most visible application-developer feature is HTTP/3 support in the Java HTTP Client API: you opt in to HTTP/3, and the client can fall back to HTTP/2 or HTTP/1.1 when needed.
var client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build();
var request = HttpRequest.newBuilder(URI.create("https://openjdk.org/"))
.GET()
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
Other Java 26 highlights are mostly continuation/cleanup work: structured concurrency is in its sixth preview, primitive types in patterns/instanceof/switch are in their fourth preview, the Vector API remains incubating, PEM encodings and lazy constants continue as previews, the Applet API is removed, and the platform starts preparing to make final mean final more consistently.
Fin
By now you should have a pretty good overview of a couple things:
-
How to install Java, which version to get and where to get it from (hint: Adoptium).
-
What a Java distribution is, which ones exist and what the differences are.
-
What the differences between the specific Java versions are.
Feedback, corrections and random input is always welcome! Simply leave a comment down below.
Thanks for reading.
Acknowledgements
Stephen Colebourne, who wrote a fantastic article on the different, available Java distributions.
There's more where that came from
I'll send you an update when I publish new guides. Absolutely no spam, ever. Unsubscribe anytime.
Comments (read-only)
104 comments
Is "Marco" in this sentence a joke or a typo? :-)
Stephen Colebourne, who wrote a fantastic article on the different, available Java distributions.
Was totally confused about Java 8, 1.8, etc until I read this. In particular the clarification of one source - openjdk too make things very clear. Thanks very much!
Could you please add java 17 features?
https://openjdk.java.net/projects/jdk/17/
I am curious about the idea of a "preview feature." I'd like to better understand. Releasing feature with unfinished syntax just seems like a way to waste my time. I don't get it.
I was wondering about the differences, because the downloads at https://jdk.java.net/ are so confusing - currently, there is the "Reference implementation" - https://jdk.java.net/java-se-ri/15 and "Ready for use" https://jdk.java.net/15/ - I've downloaded both, compared all the files by content and found they are 100% identical. So these 2 sources lead to the same OpenJDK 15 built by Oracle.
Then I downloaded "jdk-15_windows-x64_bin.zip" from Oracle Technology Network https://www.oracle.com/java/technologies/javase-jdk15-downloads.html - requires registration & login!). I compared the (Open)jdk-15 and (Oracle)jdk-15 directories by content and when ignoring dates, here are the results:
/bin folder: 45 identical files, 79 files differing by their contents. No extra or missing files found.
/lib folder: 12 identical, 8 different
/jmods folder: 0 identical, 70 different
etc.
The differences can be caused just by different text constants in the JDK source files, different compiler settings, but they are still there...
Mit freundlichen Grüßen
Ingmar Pforr
Great article! I'd love to know why there are multiple versions of JDK builds - for example if you dig through https://hub.docker.com/r/adoptopenjdk/openjdk14 you will see builds for both 14.0.1 and 14+36. Can you explain what the difference is between these two?
Gratefully yours
J
these are minor versions (14.0.1), including the build number (+36). These usually contain smaller bugfixes.
The Oracle homepage lists all those bug fixes and version differences quite nicely, have a look here:
JDK14: https://www.oracle.com/technetwork/java/javase/14u-relnotes-6361871.html
JDK11: https://www.oracle.com/technetwork/java/javase/11u-relnotes-5093844.html
I am learning and I wasn't aware that there was such significant changes from the 8 version to the 13.
let mut author = ?
I'm @MarcoBehler and I share everything I know about making awesome software through my guides, screencasts, talks and courses.
Follow me on Twitter to find out what I'm currently working on.