Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Understanding Java Specifications, APIs, JDK, and Runtime Roles

Hello! Last lesson, we compared Java’s early choices with C and C++: automatic object-memory reclamation, constrained memory access, portable class files, and a more restricted class model. Those choices are often attributed simply to “Java,” but that word can mean a language, a virtual machine, a library, or a product installed on a machine.

This lesson separates those meanings. You’ll learn which contract defines source-code behavior, which defines class files and execution, where standard library methods belong, and what the JDK and a runtime actually provide. The three sections can be studied as separate sessions of roughly 15 minutes.


Two specifications, two different questions

Imagine reviewing this Java 21 code:

import java.util.List;

class Report {
    static void print() {
        var labels = List.of("ready");
        System.out.println(labels.get(0));
    }
}

Several authorities are involved, even before the program runs. The Java Language Specification (JLS) answers questions about the source program: Is var permitted here? What type is inferred for labels? Which method does an invocation in this source denote? What must be rejected at compile time? It also defines important run-time language semantics, including expression evaluation and the memory model. “Language specification” does not mean “syntax rules only.”

Chapter 1. Introduction

Read the introduction to the Java SE 21 Language Specification for its explicit distinction between compile-time and run-time rules.

In Chapter 1, “Introduction,” read the distinction in the opening prose, before Section 1.1, “Organization of the Specification.” Also read the short closing paragraph immediately before Section 1.1, which identifies the separate specification for the usual bytecode target. Focus on what the JLS says a program means, rather than on any particular compiler’s implementation.

The Java Virtual Machine Specification (JVMS) answers a different set of questions: What is a valid class file? What instructions can it contain? How are classes loaded and linked, and what execution behavior must a JVM provide? A class file is the JVM’s input format; Java source is not. That distinction matters because a JVM can host code generated from languages other than Java, provided the resulting class files meet its requirements.

Chapter 1. Introduction

Read the JVM specification’s account of the virtual machine as an abstract target, rather than a description of one vendor’s engine.

In Section 1.2, “The Java Virtual Machine,” read the abstract machine. Continue with the class-file boundary, ending just before the paragraph beginning “The Java Virtual Machine specified here.” Notice both what the JVM accepts and what the specification leaves open about implementation.

A specification is a contract, not an executable. The JVMS does not require a particular interpreter or just-in-time compiler. HotSpot is one concrete JVM implementation; it may use interpretation and optimized native code while respecting the applicable contract. Similarly, javac is a concrete compiler, not the JLS itself.

The contracts meet at a boundary: a compiler translates source governed by language rules into class files governed by JVM rules. They are related but not interchangeable. If a source expression is illegal, consult the JLS. If a generated class file has an invalid structure, consult the JVMS. Next lesson, we’ll follow that boundary in detail from source to execution.


The standard API is neither syntax nor the JVM instruction set

Return to List.of("ready"). The dots and method-invocation expression have language rules. But java.util.List, its of method, and that method’s documented behavior belong to the Java SE standard API. The API is the platform’s specified collection of classes, interfaces, and their behaviors; a runtime supplies implementations of those platform classes.

The distinction is particularly clear with String. The language gives string expressions special treatment, but java.lang.String is also a platform class with documented methods. The JLS can constrain how certain predefined classes interact with language constructs without serving as the complete specification of every method on those classes.

Chapter 1. Introduction

Return to the Language Specification for its own explanation of where its responsibility ends and the Java SE Platform API documentation takes over.

In Section 1.4, “Relationship to Predefined Classes and Interfaces,” read the API boundary in the first paragraph. The paragraph ends before the discussion of reflection. Focus on the difference between a language rule that refers to String and a complete specification of the String API.

This separation explains two superficially similar compilation failures. If source uses a language construct unsupported by the selected source level, the issue is a language-version rule. If it uses valid syntax but calls a standard-library method unavailable in the selected platform API version, the issue is an API-version mismatch. Both may be reported by a compiler, but they point to different contracts.

It also explains why “the JVM has List.of” is imprecise. The JVMS defines how a class file refers to and invokes methods. List.of is a library method furnished by the Java SE platform. The JVM participates in executing its code; the method is not a JVM instruction.


JDK, JVM, and runtime: the installed pieces

A specification cannot compile or run an application on its own. For that you install an implementation. A JDK (Java Development Kit) supplies development tools, including javac for compilation, along with a runnable Java platform. Its java command launches applications; the JDK also includes inspection and diagnostic tools you’ll use later in the course.

A runtime environment is what an application needs when it executes: a JVM implementation together with the required platform classes, native components, and supporting data. The JVM alone is insufficient for an ordinary Java program that expects, for example, java.lang.String or java.util.List.

JVM Anatomy 101

Watch JetBrains’ “JVM Anatomy 101” for its concise explanation of why a JVM alone is not the whole execution environment.

In runtime components, listen for the distinction between the JVM and the platform libraries it needs, including core classes and native support. Stop at 00:08:28; the subsequent class-loading discussion belongs to a later lesson.

Historically, JRE was also a familiar name for a separately packaged Java Runtime Environment. Be careful not to assume that every modern Java 21 installation offers a separate JRE download with an old-style directory layout. The useful architectural distinction remains: the JDK supports development and includes a runtime; a runtime supplies what is needed to execute an application, not necessarily development tools. Modern deployments may use a runtime image assembled for a particular application.

Here is the compact version of the boundaries:

Term What it is A question it answers
JLS Specification of the Java language “Is this source legal, and what does it mean?”
JVMS Specification of the virtual machine and class-file format “Is this class file valid, and how must JVM-level operations behave?”
Java SE standard API Specification of platform classes and their behavior “What does this library type or method provide?”
JDK Installed development kit and runtime implementation “What can I use to compile, run, and inspect Java programs?”
Runtime environment Execution implementation, including a JVM and required platform components “What must be present to run this application?”

Consider one practical versioning case. By default, a Java 21 compiler emits class files targeting Java 21. A Java 17 runtime may reject one of those class files before the application reaches its business logic: the problem is the class-file version, not necessarily the source algorithm or a missing API method. Conversely, an application can have an acceptable class-file version yet still fail because a required library method is absent. The JDK’s --release option can select an older supported language level, platform API surface, and class-file target together; it cannot make external libraries or operating-system dependencies compatible by itself.

These distinctions make incident reports more useful. “Java rejected the program” leaves the responsible layer unclear. “The compiler rejected a source-level construct,” “the target runtime cannot read this class-file version,” and “the required API method is unavailable” describe different failures and suggest different remedies.


Closing perspective

“Java” spans contracts and implementations. The JLS governs Java source semantics; the JVMS governs the class-file and virtual-machine contract; the Java SE API specifies platform libraries. A JDK packages development tools with a runnable implementation, while a runtime provides the execution pieces. Keeping those layers separate lets you explain both Java’s portability promise and the version mismatches that limit it in practice.

Next lesson, we’ll trace a class across these boundaries: compilation, class-file verification, interpretation, and optimized execution.

Can't find a good explanation? Sign up and we'll make it for you

Sign up