Course outline · 0% complete

0/29 lessons0%

Course overview →

Compile first, run second

lesson 1-2 · ~7 min · 2/29

The compile mental model

Python reads your file top to bottom and executes it directly. Java adds a step in the middle, and that step is the point: it finds mistakes before the program runs, not while a customer is using it.

The pipeline has three stages:

  1. You write Main.java, plain source text.
  2. The compiler, javac, reads the whole file, checks every type and every statement terminator, and produces Main.class. That file holds bytecode, compact instructions written for the Java Virtual Machine rather than for any one physical CPU. Because the instructions are machine-neutral, the same compiled file can travel to any computer.
  3. The Java Virtual Machine, the JVM, runs the bytecode on your real machine.

The payoff is twofold. One Main.class runs on Windows, macOS, and Linux, because each machine has its own JVM that speaks its native CPU language. And because the compiler checks everything before anything runs, whole categories of typo never reach a running program.

Main.javasource you writeMain.classbytecodeJVMruns it anywherejavac compilesjava runs
One program, three stages: your source is compiled to bytecode once, then the JVM runs that bytecode on any machine.

Types are declared

The compiler can only check your program because you tell it the type of every variable at the moment you create it:

int year = 1995;
String name = "Java";

int means whole number and String means text. Compare Python's year = 1995, where the type is discovered while running.

In Java, writing year = "hello" later in the file is a compile error, so the program never runs with that mistake in it. The next unit covers all the core types.

The declaration is also documentation that cannot go stale. Reading int year tells you what the variable holds without hunting for the line that assigned it, and the compiler guarantees the answer stays true.

Compile-time errors and runtime errors

The compile step splits every mistake into two families, and knowing which family you are fighting saves debugging time daily.

FamilyCauseWhen you find out
compile-timea rule the compiler can verify by reading codebefore the program starts
runtimedepends on an actual valuewhile the program runs

A compile-time error breaks a rule the compiler can check on its own: a missing statement terminator, a misspelled name, assigning text to an int. The program never starts, and javac prints the file, the line number, and the problem.

A runtime error appears only while the program runs, because it depends on values: dividing by a variable that happens to be zero, or asking for position 10 of a 3-element array. The compiler cannot see the future, so these still crash.

Java's design pushes as many mistakes as possible into the first family. A compile error costs you seconds, while a runtime error in production can cost a company an outage.

A mistake the compiler can prove

Assigning the text "nineteen95" to a variable declared int is caught before the program ever runs.

Because types are declared, the compiler can prove by reading the code that "nineteen95" never fits in an int, which makes it a compile-time error.

Contrast that with two mistakes no compiler can rule out:

  • dividing by a zero that arrived from user input at runtime
  • indexing past the end of a list whose length is decided while running

Both depend on values that exist only during execution. The distinction is not about how serious the bug is, it is about whether the evidence is present in the source text.

A declared int in action

This program declares an int and joins it into printed text.

public class Main {
  public static void main(String[] args) {
    int year = 1995;
    System.out.println("Java was born in " + year);
  }
}

Output

Java was born in 1995

Changing 1995 to the text "nineteen95" on your own machine makes the compiler reject the program before anything executes, which is the whole lesson in one experiment.

Note that + joins the number onto the text here. Java converts the int to its printed form automatically when the other side of the + is a String, and the next lesson shows where that convenience turns into a trap.

When a typo surfaces

A typo such as Systm.out.println is found at compile time, before the program runs at all.

The compiler checks every name and every type in the whole file before producing bytecode. A misspelled class name like Systm matches nothing it knows about, so compilation fails and the program never starts.

Python behaves differently. The same typo sits harmlessly in the file until execution reaches that line, which might be after ten minutes of work or only on the rare branch that handles an error.

This is the practical meaning of "the compiler has your back". It is not that Java programmers make fewer mistakes, it is that a large class of mistakes cannot survive long enough to reach a user.