← Back to Blog

The Java Terminology Survival Guide

Java EE, Jakarta EE, javax, SE, JDK, JRE, OpenJDK, Spring Boot, Spring MVC. A plain-language guide to every confusing name in the entire Java ecosystem.

Java is a 30-year-old platform. That is plenty of time to accumulate confusing names, rebrands, overlapping projects, and terminology that makes no sense unless you were there when the decision was made. I have watched people with years of experience hesitate when asked what the difference is between Jakarta EE and Java EE, or whether they should use OpenJDK or Oracle JDK, or what exactly Spring Boot is if it is not Spring.

This post is the glossary I wish existed when I started. Every confusing name in the Java world, explained in plain language.

J2EE, Java EE, Jakarta EE: The Same Thing, Three Names

In 1999, Sun Microsystems released a set of enterprise APIs for Java. They called it J2EE, short for Java 2 Platform, Enterprise Edition. It included things like Servlets, JSP, EJB, JMS, and JDBC. The "2" in J2EE referred to Java 2, which was the marketing name for Java 1.2 through 1.4.

In 2006, Sun dropped the "2" and renamed everything. J2EE became Java EE (Java Platform, Enterprise Edition). J2SE became Java SE (Standard Edition). J2ME became Java ME (Micro Edition). The version numbers reset too. Java EE 5 was the first release under the new name, even though it was the successor to J2EE 1.4.

Then Oracle bought Sun in 2010 and inherited Java EE. By 2017, Oracle decided it did not want to keep developing Java EE internally, so it transferred the project to the Eclipse Foundation. But Oracle kept the "Java" trademark. The Eclipse Foundation could not call it Java EE anymore. They renamed it Jakarta EE.

So: J2EE, Java EE, and Jakarta EE are the same platform at different points in its life. If someone says they have J2EE experience, they mean enterprise Java. Same thing as Java EE. Same thing as Jakarta EE. Technology unchanged. The name changed twice because of corporate ownership.

The javax to jakarta Package Rename

This is where it gets painful. When Oracle donated Java EE to Eclipse, it transferred the code but not the javax namespace. The Eclipse Foundation was not allowed to release new versions of APIs under javax.*. So starting with Jakarta EE 9, every package was renamed from javax.* to jakarta.*.

That means javax.servlet.http.HttpServlet became jakarta.servlet.http.HttpServlet. Same class, same methods, different package name. If you upgrade a project from Java EE 8 to Jakarta EE 9 or later, you have to change every import. This is a mechanical find-and-replace, but it touches hundreds of files in a large project and breaks binary compatibility with older libraries.

Both namespaces still exist in the wild. Libraries targeting older Java EE versions use javax. Libraries targeting Jakarta EE 9+ use jakarta. Spring Boot 3.0 moved to Jakarta. If you are starting a new project today, you are on jakarta. If you are maintaining something built before 2022, you are probably still on javax.

Java SE, Java EE, Java ME

These are three editions of the Java platform, not three different languages.

Java SE (Standard Edition) is the core language and standard libraries. When people say "Java," they usually mean Java SE. It includes java.lang, java.util, java.io, the collections framework, concurrency utilities, the Stream API. Everything you would expect from a general-purpose language runtime. If you download a JDK and write a program that compiles with javac and runs with java, you are using Java SE.

Java EE (now Jakarta EE) is a set of specifications that extend Java SE for enterprise applications. Servlets, JPA, JMS, CDI, JAX-RS, and the rest. Java EE is not a separate runtime. It is a collection of APIs that run on top of Java SE, usually inside an application server like WildFly, Payara, or Open Liberty. You can also use individual Java EE specifications outside an application server, which is what most modern projects do through frameworks like Spring.

Java ME (Micro Edition) was designed for embedded devices and mobile phones. It mattered in the pre-smartphone era when Java ran on Nokia handsets and set-top boxes. It is mostly irrelevant today unless you work with legacy embedded systems.

Java Version Numbers: Why They Make No Sense

Java versions have gone through three different numbering schemes in 30 years, and none of them are consistent with each other.

Phase 1: The 1.x era (1996 to 2004). Java 1.0, 1.1, 1.2, 1.3, 1.4. These make sense on their own. But Sun decided that Java 1.2 was such a big deal that they started marketing it as "Java 2." So Java 1.2 through 1.4 were all called Java 2 Platform. The internal version was still 1.x, but the marketing name was Java 2. This is where J2SE and J2EE came from.

Phase 2: The drop-the-one era (2004 to 2017). Java 1.5 was marketed as Java 5. Java 1.6 was Java 6. Java 1.7 was Java 7. Java 1.8 was Java 8. The internal version string still said 1.8.0, but everyone called it Java 8. This era produced multi-year gaps between releases. Java 6 came out in 2006. Java 7 in 2011. Java 8 in 2014. Java 9 in 2017.

Phase 3: The six-month cadence (2017 to now). Starting with Java 9, Oracle switched to a new release every six months. Java 10 in March 2018, Java 11 in September 2018, and so on. The version number now just increments by one every six months. Java 21 came out in September 2023. Java 25 is due in September 2025.

LTS vs Non-LTS

Not all versions are created equal. Oracle designates certain releases as Long-Term Support. LTS releases get security patches and bug fixes for years. Non-LTS releases get six months of support and then they are done.

The current LTS releases are Java 8, 11, 17, 21, and 25 (starting September 2025). If you are choosing a version for production, pick an LTS release. Non-LTS versions like Java 18 or Java 20 are fine for experimentation but you do not want to run them in production because they stop getting patches almost immediately.

Most of the industry runs on Java 17 or 21 today. Java 8 is still out there in legacy systems that were never migrated, and there are more of those than anyone wants to admit.

JDK vs JRE vs JVM

These three abbreviations refer to different layers of the same stack, and people use them interchangeably when they should not.

JVM (Java Virtual Machine) is the runtime engine that executes Java bytecode. It handles memory allocation, garbage collection, just-in-time compilation, plus thread management. The JVM is a specification, not a single implementation. HotSpot, OpenJ9, and GraalVM are all JVM implementations. When someone says "JVM language," they mean a language that compiles to bytecode and runs on the JVM, like Kotlin, Scala, or Clojure.

JRE (Java Runtime Environment) is the JVM plus the standard class libraries. It is everything you need to run a Java program, but not enough to compile one. Until Java 8, the JRE was distributed separately from the JDK. You could install just the JRE on a production server if you only needed to run applications, not develop them. Starting with Java 11, the standalone JRE was removed. If you want to run Java, you install a JDK.

JDK (Java Development Kit) is the JRE plus development tools: javac (compiler), jdb (debugger), jar (archiver), jlink (custom runtime builder), and other utilities. This is what you install when you develop Java applications. Since Java 11, the JDK is the only distribution. There is no separate JRE download anymore.

In practice today, you install a JDK and that is it. The JRE/JDK distinction only matters if you are working with Java 8 or earlier, or if you are building minimal Docker images with jlink.

OpenJDK vs Oracle JDK vs Everything Else

This is the one that confuses people the most, because the answer has changed multiple times.

OpenJDK is the open-source reference implementation of Java SE. It is the upstream source code from which nearly every JDK distribution is built. Think of it as the Linux kernel of the Java world. You can build it yourself from source, but most people do not.

Oracle JDK is Oracle's own build of OpenJDK with some additional tooling and support. Until Java 11, Oracle JDK had features that OpenJDK did not, like Flight Recorder and Mission Control. Starting with Java 11, the two have the same features. The difference is licensing. Oracle JDK is free for development and testing, but requires a paid license for production use under Oracle's current terms (which have changed multiple times, so always check the latest). Oracle also offers free production use through Oracle OpenJDK builds, which are separate from Oracle JDK. Yes, Oracle distributes two different JDKs. It is confusing because it is confusing.

Because Oracle's licensing situation has been a moving target, the industry has largely moved to third-party OpenJDK builds:

  • Eclipse Temurin (Adoptium) is the most popular community build. Free, open-source, well-tested. This is what most teams use today.
  • Amazon Corretto is Amazon's build, optimized for AWS. Free, includes long-term support. A good default if you run on AWS.
  • Azul Zulu is Azul's build with commercial support options. Popular in enterprise environments.
  • GraalVM is Oracle's high-performance JDK with an advanced JIT compiler and native image compilation (native-image compiles Java to standalone executables with no JVM). It is a JDK and more. GraalVM Community Edition is free. Enterprise Edition requires a license.

All of these are built from the same OpenJDK source code. They differ in build optimizations, support terms, update cadence, and licensing. For most projects, Temurin is the safe default. If your cloud provider offers a JDK (Corretto on AWS, Microsoft Build of OpenJDK on Azure), using it is a reasonable choice because you get support alignment with your infrastructure.

Spring vs Spring Boot vs Spring Framework vs Spring Cloud

Spring is a collection of projects, not a single one, and the naming is a mess.

Spring Framework is the original project. It started in 2003 as a reaction to the complexity of J2EE. Fundamentally, it provides dependency injection and aspect-oriented programming. Over the years it grew to include Spring MVC (web framework), Spring JDBC (database access), Spring Security, and dozens of other modules. When someone says "Spring" without qualification, they usually mean Spring Framework.

Spring Boot is an opinionated layer on top of Spring Framework that auto-configures everything. Instead of writing XML or Java configuration to wire up a web server, database connection pool, and security filters, you add starter dependencies and Spring Boot configures sensible defaults. The framework also embeds a web server (Tomcat, Jetty, or Netty) so you can run your application as a standalone JAR instead of deploying a WAR to an application server. Spring Boot did not replace Spring Framework. It made it dramatically easier to use.

Spring MVC is the traditional web framework inside Spring Framework. It uses a servlet-based, thread-per-request model. You write controllers with @RestController, return objects, and Spring serializes them to JSON. This is what most Spring Boot web applications use.

Spring WebFlux is the reactive web framework, also inside Spring Framework. It uses a non-blocking, event-loop model (Reactor/Netty). It exists for high-concurrency scenarios where you need to handle thousands of connections with minimal threads. Most applications do not need it. If you are not sure whether you need WebFlux, you do not need WebFlux.

Spring Cloud is a separate project that adds distributed systems patterns on top of Spring Boot: service discovery, circuit breakers, distributed configuration, API gateways. It is for microservice architectures. You can use Spring Boot without Spring Cloud, and most projects do.

The hierarchy is: Spring Framework is the foundation. Spring Boot sits on top and makes it easy to use. Spring MVC and Spring WebFlux are two alternative web layers inside Spring Framework. Spring Cloud extends Spring Boot for distributed systems. You almost always want Spring Boot. Whether you need the others depends on your architecture.

WAR vs JAR vs EAR vs Fat JAR

JAR (Java Archive) is a ZIP file containing compiled .class files, resources, and a manifest. It is the standard packaging format for Java libraries and applications. You can run an executable JAR with java -jar app.jar if it has a main class defined in its manifest.

WAR (Web Application Archive) is a JAR with a specific directory structure for web applications. It contains a WEB-INF directory with web.xml (deployment descriptor), compiled classes, and dependency JARs. WAR files are deployed to application servers like Tomcat, WildFly, or WebSphere. The application server provides the servlet container and manages the application lifecycle.

EAR (Enterprise Application Archive) is a container that bundles multiple JARs and WARs together into a single deployable unit for a Java EE application server. It was designed for large enterprise applications with multiple modules. EAR files are mostly a relic of the J2EE era. Modern architectures rarely use them.

Fat JAR (also called uber JAR or shaded JAR) is a JAR that includes all of its dependencies inside the archive. Instead of deploying a WAR to an application server, you bundle your application, its dependencies, and an embedded web server into a single JAR and run it with java -jar. This is what Spring Boot produces by default. It is the standard deployment model for modern Java applications.

The evolution here tells a story. J2EE applications were WARs and EARs deployed to heavy application servers. Modern Java applications are fat JARs that run standalone. The application server is inside the JAR now, not around it.

JDBC vs JPA vs Hibernate vs Spring Data

Four layers of database access that build on each other, and people regularly confuse which layer they are actually using.

JDBC (Java Database Connectivity) is the lowest-level API. It is part of Java SE. You open a connection, write SQL strings, execute them. Result sets come back as a cursor you iterate. It is verbose but direct. You have full control over every query. JDBC is always there, whether you use it directly or not.

JPA (Java Persistence API) is a specification (part of Jakarta EE) for object-relational mapping. You annotate Java classes with @Entity, @Table, @Column, and JPA maps them to database tables. You write JPQL (Java Persistence Query Language) instead of raw SQL, and JPA translates it. JPA is a specification, not an implementation. It defines interfaces and behavior. Someone else provides the code that makes it work.

Hibernate is the most popular implementation of JPA. When you use JPA in a Spring Boot application, Hibernate is almost certainly the provider running underneath. Hibernate existed before JPA and has its own proprietary API, but most people use it through the JPA interfaces. Saying "I use JPA" and "I use Hibernate" usually means the same thing in practice.

Spring Data JPA is a Spring project that sits on top of JPA (and so on top of Hibernate). It generates repository implementations from interface method names. You declare findByEmailAndStatus(String email, Status status) on an interface and Spring Data JPA creates the query for you. The library also provides pagination, sorting, and specification-based queries out of the box.

Spring Data JDBC is a different Spring Data module that uses JDBC directly, without JPA or Hibernate. It maps objects to tables through conventions instead of annotations, avoids lazy loading and the JPA session cache, and gives you simpler, more predictable database access. It is a good choice when JPA's complexity is more than you need.

The stack from bottom to top: JDBC talks to the database. Hibernate implements the JPA specification on top of JDBC. Spring Data JPA generates repositories on top of JPA. Or you skip JPA entirely and use Spring Data JDBC, which goes straight to JDBC with minimal magic.

Date vs LocalDate vs sql.Date

Java has three classes named Date and they are all different.

java.util.Date is the original date class from Java 1.0. It represents a point in time as milliseconds since January 1, 1970 UTC. Despite its name, it includes both date and time information. It is mutable, poorly designed, and has been effectively deprecated since Java 8. Most of its methods are literally marked @Deprecated. Do not use it in new code.

java.sql.Date extends java.util.Date and represents a date without a time component, specifically for JDBC. It exists because SQL has a DATE type that does not include time, and java.util.Date always includes time. It inherits all the problems of java.util.Date plus its own. Also do not use it in new code.

java.time.LocalDate is the correct class for representing a date without time. It was introduced in Java 8 as part of the java.time package (designed by the creator of Joda-Time). It is immutable, thread-safe, and well-designed. If you need a date, use LocalDate. If you need date and time, use LocalDateTime or ZonedDateTime. If you need an instant in time, use Instant. The java.time package replaced the old date APIs completely, and every modern library and framework supports it.

If you see java.util.Date or java.sql.Date in code written after 2014, it is legacy code or someone who did not get the memo.

Stream vs InputStream

These share a name and nothing else.

java.util.stream.Stream was introduced in Java 8 for functional-style operations on collections. You call .stream() on a list, chain .filter(), .map(), and .collect(), and get declarative data transformation. It has nothing to do with I/O. The name "stream" refers to a stream of data elements flowing through a processing pipeline.

java.io.InputStream and java.io.OutputStream are the original I/O abstractions from Java 1.0 for reading and writing bytes. File reading, network sockets, HTTP request bodies. They are about moving bytes between your program and the outside world.

When someone says "Java streams" without context, ask which one they mean. In a data processing discussion, it is java.util.stream. In an I/O or networking discussion, it is java.io.

Future vs CompletableFuture vs Virtual Threads

Three different approaches to asynchronous programming, each from a different era of Java.

Future<T> was introduced in Java 5. It represents a computation that will complete at some point. You submit a task to an ExecutorService, get back a Future, and call .get() to block until the result is available. The problem is that blocking is all you can do. There is no way to chain operations, combine futures, or handle errors without blocking a thread. It is limited to the point of being frustrating.

CompletableFuture<T> was introduced in Java 8 and fixes almost everything wrong with Future. You can chain operations with .thenApply(), combine multiple futures with .allOf(), handle errors with .exceptionally(), and compose asynchronous pipelines without blocking. It is Java's answer to JavaScript's Promises. If you are writing async code on Java 8 through 20, CompletableFuture is the primary tool.

Virtual Threads (Project Loom) arrived in Java 21 as a permanent feature and changed the game. Virtual threads are lightweight threads managed by the JVM, not the operating system. You can create millions of them without running out of memory. The key insight is that with virtual threads, you do not need CompletableFuture for most async work. You just write normal blocking code, run it on a virtual thread, and the JVM handles the rest. Thread.sleep(), JDBC calls, HTTP requests, all of it becomes non-blocking under the hood without changing your code.

Virtual threads do not replace CompletableFuture in every case. If you need to explicitly compose parallel operations or build reactive pipelines, CompletableFuture still has a role. But for the common case of "make a blocking call without tying up an OS thread," virtual threads are simpler and more natural.

Bean vs JavaBean vs Spring Bean vs EJB

Four things called "bean" with four different meanings.

JavaBean is a convention from the late 1990s. A JavaBean is a class with a no-argument constructor, private fields, and public getters and setters following a naming pattern (getName(), setName()). The class also implements Serializable. The convention was designed for visual builder tools that could inspect and wire up components. Most of those tools are gone, but the convention survived and became the default pattern for DTOs and data classes in Java. When people say POJO (Plain Old Java Object), they often mean a JavaBean.

Spring Bean is any object managed by the Spring IoC container. It does not have to follow the JavaBean convention. A Spring bean is usually created by annotating a class with @Component, @Service, or @Repository, or by defining a method with @Bean in a configuration class. The Spring container creates the instance, manages its lifecycle, and injects it where needed. Spring beans are about dependency injection, not getter/setter conventions.

EJB (Enterprise JavaBean) is a server-side component model from the Java EE specification. EJBs were the primary way to write business logic in J2EE applications. They came in three flavors: session beans (stateful and stateless), message-driven beans, and entity beans. EJBs required application servers, XML deployment descriptors, and a lot of ceremony. Their complexity was the primary motivation for creating Spring in the first place. EJB 3.0 (2006) cut a lot of the ceremony by introducing annotations, but by then Spring had already won. Modern Java applications rarely use EJBs directly.

The word "bean" in Java has been overloaded to the point where it just means "a thing that the framework manages." Context determines which kind.

Project Loom, Panama, Valhalla, Amber

These are codenames for major ongoing efforts to evolve the Java language and runtime. They are not versions or products. Each one addresses a different fundamental limitation.

Project Loom added virtual threads and structured concurrency to Java. Virtual threads shipped in Java 21. Structured concurrency (a way to manage groups of concurrent tasks as a unit) is still in preview. Loom is about making concurrent programming simpler by eliminating the need to manage thread pools and async callbacks for most use cases.

Project Panama is about connecting Java to native code and data. It replaces JNI (Java Native Interface), which has been the only way to call C/C++ code from Java for 25 years and is universally disliked. Panama introduced the Foreign Function & Memory API, which provides safe, efficient access to native memory and native functions without writing C glue code. It shipped as a permanent feature in Java 22.

Project Valhalla is about value types and specialized generics. Today, Java generics only work with objects, not primitives. You cannot write List<int>, only List<Integer>, which means boxing every element. Valhalla introduces value classes (flat, identity-free types that can be stored inline like primitives) and eventually primitive types in generics. This is the hardest of the four projects and has been in development the longest. It will fundamentally change Java's memory model when it ships.

Project Amber is a collection of language productivity features. Records, sealed classes, pattern matching, switch expressions, and text blocks all came from Amber. These are the features that make modern Java code visibly different from Java 8 code. Amber is not a single big change but a steady stream of smaller ones that collectively modernize the language syntax.

You do not "use" these projects directly. You use the features they produce when those features land in a Java release. Loom gave you virtual threads in Java 21. Panama gave you the Foreign Function API in Java 22. Amber has been shipping features since Java 14. Valhalla is still in progress.

The Naming Is the Problem

Most of this confusion exists not because Java is complicated, but because Java is old and has been through multiple corporate owners, multiple eras of software architecture, and multiple attempts to modernize without breaking backward compatibility. Every rebrand, every specification-versus-implementation split, every "we need a new name because of trademark law" decision adds another layer of terminology that developers have to parse.

The technology underneath is solid. The names are the hard part. If this post saves you one confused meeting or one wrong Google search, it did its job.

Share
X LinkedIn HN
UI

Umur Inan

Principal Software Engineer

Backend engineer focused on JVM systems, distributed architecture, and the failure modes that only show up in production. I write about what I learn building and breaking things at scale.

👁 0 17 min read

Comments (0)