The JVM, the JDK, and How a Java Program Runs
What actually happens between writing Java source code and seeing a program run — and why that model makes Java portable.
What you'll learn
- Explain the difference between the JDK, the JVM, and bytecode
- Describe what javac and java each do, in order
- Read and predict the structure of a small multi-class Java program
Explanation
Java code goes through two distinct steps before it does anything: compilation, then execution — and understanding what happens in each step explains most of what makes Java behave the way it does. The JDK (Java Development Kit) is the toolset you install: it bundles a compiler (javac), the runtime needed to execute programs, and supporting tools. When you run javac Greeter.java, the compiler doesn't produce machine code for your specific CPU — it produces bytecode, a compact, platform-neutral instruction format saved in a .class file. That bytecode is not directly executable by your operating system; it's executable by the JVM (Java Virtual Machine), a program that reads bytecode and either interprets it or compiles pieces of it to real machine code on the fly (the just-in-time, or JIT, compiler) as your program runs.
This two-step model — compile once to bytecode, run that same bytecode on any JVM — is the literal mechanism behind Java's "write once, run anywhere" promise. The .class file you produce on one operating system runs unmodified on any other operating system that has a matching JVM installed, because the JVM is what absorbs the platform-specific differences, not your compiled code. The command java Greeter starts a JVM, loads Greeter.class, and calls its public static void main(String[] args) method — the fixed entry point every runnable Java program needs.
A Java source file has a small number of required, position-sensitive parts: an optional package declaration first, then any import statements, then exactly one public top-level class whose name must match the filename exactly (Greeter.java must contain public class Greeter) — this is a compiler-enforced rule, not a convention. A single file can contain more than one class, but only one may be public; the compiler produces a separate .class file for every class, public or not, which is why a small multi-file program still produces several .class files.
Example
This models the compile-then-run pipeline as data, not real Java — the real syntax appears in this lesson's guided local lab.
// A simplified model of javac + java, as a two-stage pipeline.
function compile(sourceFileName, sourceContainsPublicClass) {
if (!sourceContainsPublicClass) throw new Error("no public class found");
return { bytecodeFile: sourceFileName.replace(".java", ".class") };
}
function run(bytecodeFile) {
return `JVM loads ${bytecodeFile} and calls its main method`;
}
const compiled = compile("Greeter.java", true);
console.log(run(compiled.bytecodeFile));
// -> "JVM loads Greeter.class and calls its main method"Try it yourself
Change the filename so it no longer matches the public class name, and predict what compile() should do.
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Guided exercise
Guided exercise
Write classFileFor(sourceFileName) that returns the .class filename javac would produce for a source file's PUBLIC class — i.e. it strips '.java' and appends '.class'. Assume the input always ends in '.java'.
Checks: Greeter.java -> Greeter.class · Main.java -> Main.class
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Independent exercise
Independent exercise
Write findEntryPoint(classNames, publicClassName) that returns publicClassName if it appears in classNames (the JVM can only start from the class you name on the command line, and it must exist), otherwise returns null.
Checks: returns the class name when present · returns null when the class isn't in the list
Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.
Stuck? Get a hint.
Guided local lab
Compile and Run a Multi-Class Java Application Locally
Runs on your computerYou'll write two classes — a Greeter that builds messages and a Main that calls it — compile both with javac, and run the program with java, entirely on your own machine.
Required tools
- JDK (21 LTS or newer)
- A terminal (any)
- A text editor or IDE (any (e.g. VS Code, IntelliJ IDEA Community))
Setup
- Confirm your JDK is installed: run `java -version` and `javac -version` — both should print 21 or higher.
- Create a project folder named greeting-app.
- Inside it, create a src folder for your .java source files.
Project structure
greeting-app/
src/
Greeter.java
Main.javaStarter files
src/Greeter.java
public class Greeter {
// TODO: add a static method buildMessage(String name) that returns
// "Hello, " + name + "!" -- and call it from Main.
}
src/Main.java
public class Main {
public static void main(String[] args) {
// TODO: call Greeter.buildMessage(...) with a name and print the result.
// TODO: also print how many command-line arguments were passed (args.length).
}
}
Requirements
- Greeter.java defines a public class Greeter with a static method buildMessage(String name) returning a greeting string.
- Main.java defines the program's entry point and calls Greeter.buildMessage(...).
- Running the program with at least one command-line argument prints that argument's count.
- The program compiles with zero warnings and runs without throwing.
Commands to run
Compile both source files into .class files
javac -d out src/Greeter.java src/Main.javaRun the compiled program, passing one argument
java -cp out Main Alex
Expected behavior
javac produces Greeter.class and Main.class inside out/ with no errors. Running `java -cp out Main Alex` prints a greeting containing "Alex" and a line reporting 1 argument.
Verify it yourself
ls outExpected: Greeter.class and Main.class are both listed
java -cp out Main AlexExpected: Prints a greeting mentioning Alex, then a line stating 1 argument was passed
java -cp out MainExpected: Prints a greeting (using a default or empty name) and reports 0 arguments, without crashing
Troubleshooting
- `error: class Greeter is public, should be declared in a file named Greeter.java` — The public class name must exactly match its filename, including case.
- `Error: Could not find or load main class Main` — Make sure you pass -cp out (the folder containing the .class files) and the bare class name Main, not Main.java or out/Main.class.
- `javac: file not found: src/Greeter.java` — Run javac from the greeting-app folder, not from inside src/.
Stuck? Get a hint.
Extension challenge
Add a third class, Farewell, with a static method buildFarewell(String name), and call it from Main right after the greeting so the program prints both a hello and a goodbye.
When you've verified this locally, use the "Mark lesson complete" button below to record your progress.
Common mistakes
- Naming the file differently from its public class (Java requires an exact, case-sensitive match).
- Running `java Main.java` instead of `java Main` — java takes a class name, not a filename (a newer single-file launch mode accepts .java files directly, but that's a different mechanism than the standard compile-then-run workflow this lesson teaches).
- Forgetting that every class you reference must itself be compiled — a NoClassDefFoundError almost always means a needed .class file is missing from the classpath.
Knowledge check
Takeaway
Java source compiles to platform-neutral bytecode, and a JVM — one per platform — is what actually executes it; that split is the entire mechanism behind Java's portability.
Summary
javac turns .java source into .class bytecode; java starts a JVM that loads that bytecode and runs its main method. A public class's name must match its filename exactly, and every referenced class needs its own compiled .class file.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.