algoz
Chapters/00 Start here/Set up Java
00 · Start here — note 3 of 3
Concept · 10 min

Set up Java

Install a JDK, pick an editor, and run your first program from the terminal.

You'll gain
Compile and run Java yourselffrom the terminal and an editor
Practice
Sheet coming sooncoding problems for this note
Summary

Install a JDK, write a .java file, javac turns it into bytecode, and java starts the JVM that runs it.

The idea in real life

Think of a recipe written once on a standard card that any trained cook can read. You send the same card to kitchens in many cities, and the cook in each kitchen makes the dish with that kitchen's own stove and pans. Your source file is the recipe you wrote, bytecode is the standard card, and the JVM is the cook on each computer.

Why it exists

  1. You need a working JDK and an editor to write, run, and experiment with every program in this course.
  2. Compile errors and runtime errors happen at different stages, so knowing which tool generated an error stops you from looking in the wrong place.
  3. Running code from the terminal shows you each step clearly, before an editor hides them behind a run button.
  4. Knowing how your code becomes bytecode and runs inside the JVM makes later topics, such as memory in chapter 02, easier to follow.

How it works

JDK, JRE and JVM

Three names for the tools that build and run Java.

To write Java, you need tools to turn text into instructions and an engine to execute those instructions. Java divides these responsibilities across three layers:

  • JVM (Java Virtual Machine): The execution engine. It reads compiled bytecode and executes the instructions on your computer's operating system and processor.
  • JRE (Java Runtime Environment): Historically, the package containing the JVM together with Java's standard libraries, used only to run pre-compiled Java applications.
  • JDK (Java Development Kit): The complete toolkit for developers. It includes the compiler (javac), analysis tools (javap), packaging tools (jar), and a built-in runtime engine (the JVM and standard libraries).

Since Java 11, Oracle no longer offers a separate JRE download, though some vendors still do. A JRE cannot compile code, so you install a JDK.

ToolWhat it does
javacCompiles .java source code into .class bytecode files
javaStarts the JVM, loads classes, and runs your program
JVMExecutes bytecode instructions and translates hot code to machine code
javapDisassembles .class files to inspect their bytecode instructions
JDKThe developer bundle containing javac, java, javap, and core libraries

Install a JDK

Choose a long-term support release from a free build provider.

Java releases a new version every six months, but certain versions receive LTS (Long-Term Support) designations every two years. LTS releases receive stability updates and security patches for years. The programs in this course require JDK 17 or newer. Install the newest LTS release, JDK 25 (JDK 21 is also fine).

We recommend Eclipse Temurin from Adoptium (https://adoptium.net), an open-source, vendor-neutral build of OpenJDK maintained under the Eclipse Foundation. Builds from Oracle, Amazon Corretto, and Azul Zulu are also fine.

  • Windows: Download and run the .msi installer from Adoptium (ensure "Add to PATH" and "Set JAVA_HOME" remain checked during setup). Alternatively, run Windows Package Manager in your terminal:
    Terminal
    winget install -e --id EclipseAdoptium.Temurin.21.JDK
    The number in the id is the Java version; change 21 to 25 for the newer LTS.
  • macOS: Download the .pkg installer from Adoptium, or install with Homebrew:
    Terminal
    brew install --cask temurin
  • Linux: On Debian or Ubuntu, install through the Adoptium package repository (sudo apt install temurin-21-jdk, or temurin-25-jdk for the newer LTS) or your distribution's package manager (sudo apt install default-jdk). On Fedora or RHEL, install using sudo dnf install temurin-21-jdk.

Check that it works

Confirm the compiler and runtime are visible in your terminal.

Open a fresh terminal window after installation and run two verification commands:

Terminal
java -version
javac -version

When Java is installed correctly, java -version prints three lines displaying the version number (such as 17, 21, or 25), the runtime environment name, and the 64-bit Server VM identifier. Running javac -version prints javac followed by the matching version number. If the terminal prints an error that the command is not recognized, the installation folder is not yet on your system path.

Your first program

Write source text, compile to bytecode, and launch the runtime.

In Java, every runnable program lives inside a class, so public class Main creates the container matching our file name. The main method is the entry point where the JVM begins executing code. Chapter 01 teaches classes, methods, and statements in depth.

Create a file named Main.java and enter this program:

Main.java
public class Main {
    public static void main(String[] args) {
        System.out.println("Algoz setup check: ready.");
        System.out.println("Java environment working.");
    }
}
Output
Algoz setup check: ready.
Java environment working.

In your terminal, navigate to the folder containing Main.java and run:

Terminal
javac Main.java
java Main

The first command, javac Main.java, compiles the source file into a binary file named Main.class in the same directory. The second command, java Main, starts the JVM and runs the bytecode inside Main.class. Notice you pass the class name Main, not the file name Main.class.

Pick an editor

Choose a capable editor, but stay fluent without autocomplete.

Two reliable options:

  • IntelliJ IDEA from JetBrains: an editor built for Java, with code checks and a debugger. It can be used for free; check the current editions on https://www.jetbrains.com/idea/.
  • Visual Studio Code: A free code editor from Microsoft. Install the Extension Pack for Java from the Extensions marketplace to add language support, project creation, and debugging.

Also practise in a plain editor with no autocomplete. Many interview editors have little or none, and if you depend on it, coding in a round will feel slow.

Going deeper

Running a single file directly

Run one file in a single step, with no class file.

Since JDK 11 (JEP 330: Launch Single-File Source-Code Programs), the java launcher can execute a single .java file directly:

Terminal
java Main.java

When you run this command, the launcher compiles the source code directly in memory and runs it immediately. It creates no .class file on disk. This is convenient for testing short algorithms and quick experiments.

Since JDK 22 (JEP 458: Launch Multi-File Source-Code Programs), the launcher can also resolve and run programs that span multiple source files without an explicit compile step. Traditional compilation with javac remains the standard for multi-file projects, build automation tools, and production applications.

PATH and JAVA_HOME

How your computer finds the Java tools.

PATH is an environment variable that holds an ordered list of folder directories. When you type javac or java without an absolute path, the operating system checks each directory in PATH until it finds an executable with that name.

JAVA_HOME is an environment variable that points to the root directory where your JDK is installed (for example, C:\Program Files\Eclipse Adoptium\jdk-21.0.x on Windows or /usr/lib/jvm/temurin-21 on Linux). Build tools like Maven and Gradle, along with your editor, check JAVA_HOME to locate compiler tools and standard libraries.

If your terminal reports that javac is not recognized or not found, find the bin directory inside your JDK folder (such as %JAVA_HOME%\bin on Windows or $JAVA_HOME/bin on Linux), add that path to your user or system PATH variable, and open a brand-new terminal window.

Compile errors versus runtime errors

Some mistakes stop the compiler; others only fail when the program runs.

Software errors in Java fall into two distinct categories:

  • Compile errors: Detected by javac while reading source text. If your code violates syntax rules, misspells a type, or tries to assign an invalid type, the compiler refuses to generate bytecode. No .class file is written.
  • Runtime errors: Occur after the program compiles successfully. The bytecode is valid, but an illegal operation occurs while the JVM executes instructions. The JVM halts execution and prints an unhandled exception stack trace.

The following program compiles cleanly because integer division is valid syntax. However, when the JVM executes the division instruction with a denominator of zero, it throws an unhandled exception:

RuntimeErrorDemo.java
public class RuntimeErrorDemo {
    public static void main(String[] args) {
        int numerator = 10;
        int denominator = 0;
        int result = numerator / denominator;
        System.out.println(result);
    }
}

When you run java RuntimeErrorDemo, the JVM stops and prints:

text
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at RuntimeErrorDemo.main(RuntimeErrorDemo.java:5)

The stack trace identifies the active thread (main), the exception class (java.lang.ArithmeticException), the specific reason (/ by zero), and the file and line number (RuntimeErrorDemo.java:5) where execution failed.

Under the hood

From source to bytecode

javac turns your code into small instructions for the JVM.

When you execute javac Main.java, the compiler parses your source text, checks types and variable references, and emits compact instructions called bytecode into Main.class.

Bytecode is an intermediate language designed for a virtual stack machine rather than any physical CPU. Each instruction consists of a 1-byte operation code (opcode) followed by optional operands.

You can inspect the compiled bytecode yourself using javap, the disassembler included with every JDK. Running javap -c Main produces output like this:

text
Compiled from "Main.java"
public class Main {
  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #13                 // String Algoz setup check: ready.
       5: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

In this method:

  • getstatic pushes the static field reference System.out onto the operand stack.
  • ldc loads the string constant "Algoz setup check: ready." from the class constant pool onto the stack.
  • invokevirtual calls the println method using the stream and string currently on the stack.
  • return exits the method.

What the JVM does when it starts

Load, check, run, then speed up the busy parts.

When you run java Main, the JVM performs four stages to execute your code:

  1. Loading: The ClassLoader reads Main.class from disk or memory, parses its binary structure, and creates internal class representations in memory (the metaspace).
  2. Bytecode verification: Before any instruction runs, the verifier checks the bytecode to ensure it does not corrupt memory, violate access rules, or underflow the operand stack.
  3. Interpretation: The JVM interpreter begins reading opcodes sequentially, translating them to native processor operations one by one so the program starts immediately.
  4. JIT compilation: As execution continues, the JVM monitors which methods and loops run frequently (called "hot spots"). The Just-In-Time (JIT) compiler compiles those hot bytecode sequences directly into native CPU instructions, allowing frequently executed loops to run at near-native speed.

While your program runs, the JVM's garbage collector automatically identifies objects that your code no longer references and reclaims their heap memory (chapter 02 explores object memory).

Bytecode is the same on every machine

One class file runs on every operating system.

The core design of Java is "Write Once, Run Anywhere" (WORA).

The same JDK version produces the same Main.class whether you compile on Windows, macOS or Linux. Bytecode does not know about Intel, AMD, or Apple Silicon instruction sets.

Portability works because the JVM itself is platform-specific. Oracle and Adoptium maintain distinct JVM binaries tailored to Windows, macOS, and Linux hardware architectures. Each platform-specific JVM knows how to translate the universal bytecode format into instructions for its host machine.

Class file versions

A newer JVM runs older class files, never the other way round.

Every .class file begins with the 4-byte magic number 0xCAFEBABE, followed by a 2-byte minor version and a 2-byte major version (defined in Chapter 4 of the JVM Specification).

The major version indicates the JDK release used to compile the file:

  • Java 8: 52
  • Java 11: 55
  • Java 17: 61
  • Java 21: 65
  • Java 25: 69

The JVM is strictly backward-compatible: a Java 21 runtime can load and execute class files compiled with Java 17, 11, or 8. However, it is not forward-compatible. If you compile code on JDK 21 (major version 65) and try to run it on a machine with JDK 17 (which only recognises versions up to 61), the JVM refuses to load the class:

text
Error: LinkageError occurred while loading main class Main
  java.lang.UnsupportedClassVersionError: Main has been compiled by a more recent version of the Java Runtime (class file version 65.0), this version of the Java Runtime only recognizes class file versions up to 61.0

When you see UnsupportedClassVersionError, your runtime java command is older than the javac compiler that produced the .class file.

In Java

Main.java
public class Main {
    // Calculates and returns the arithmetic sum of integers from 1 up to limit.
    static int calculateSum(int limit) {
        int sum = 0;
        for (int i = 1; i <= limit; i++) {
            sum += i;
        }
        return sum;
    }

    public static void main(String[] args) {
        int count = 10;
        int total = calculateSum(count);

        System.out.println("Environment verification: SUCCESS");
        System.out.println("Calculated sum: " + total);
    }
}
Output
Environment verification: SUCCESS
Calculated sum: 55

Traps

  1. File name does not match the public class name

    In Java, a file containing a public class Main must be named exactly Main.java, matching uppercase and lowercase letters. If the file is named main.java or App.java, the compiler rejects it.

  2. Adding .class when launching the program

    The javac compiler expects a file name with its extension (javac Main.java). The java runtime expects the name of a compiled class, not a file on disk. Passing .class causes Error: Could not find or load main class Main.class.

    ✕ WRONGjava Main.class
    ✓ RIGHTjava Main
  3. Installing only a JRE instead of a JDK

    A JRE contains only the runtime engine needed to run existing Java applications. It does not include javac. Without a full JDK, you cannot compile .java files from your terminal or command prompt.

  4. Multiple JDKs installed with PATH pointing to an older version

    If an older JDK was previously installed, your operating system may still resolve java or javac to the older directory. Check which binary is active with where java on Windows or which java on macOS and Linux, and adjust your PATH order so the new JDK appears first.

  5. Relying exclusively on the editor run button

    The run button hides the compile and run steps. When it fails, or when you work somewhere without it, you need to know the two commands underneath.

  6. Depending on IDE autocomplete for common methods

    If you never type method names and imports yourself, you will not remember them in a round. Practise without completion, as said in "Pick an editor".

AlgozSet in Geist & Geist MonoJava track