Java • Debugging and Testing

Compilation vs. Runtime Errors — Interpret Java compilation errors vs runtime exceptions

Learn to distinguish errors caught before your program runs from those that crash it mid-execution.

Java programming emblem with a coffee cup, code braces, and terminal
Inside this lesson 10 sections 5 questions 4 figures

SECTION 1

Historical Context & Motivation

In the earliest days of computing, programmers wrote instructions in raw binary or assembly language, and errors were painfully difficult to find. There was no separate step that checked your code for mistakes before running it—programs simply crashed or produced garbage output when something was wrong. As programming languages evolved, designers realized that catching mistakes before a program runs could save enormous amounts of time and frustration. This idea led to the development of compilers, tools that translate human-readable source code into machine code while also checking for certain categories of mistakes along the way.

Java, created by James Gosling at Sun Microsystems in 1995, was designed with a strong type system and a two-phase execution model: first the compiler (javac) checks your code and converts it to bytecode, and then the Java Virtual Machine (JVM) runs that bytecode. This split creates two distinct moments when errors can appear—at compile time and at runtime. Understanding when and why each type of error occurs is one of the most important debugging skills you can develop.

1957 • FORTRAN Compiler Released

IBM releases the first widely used compiler, proving that automatic error-checking before execution is possible and valuable.

1972 • C Language Created

Dennis Ritchie creates C, a compiled language that introduces stronger compile-time type checking but still allows many runtime errors like buffer overflows.

1995 • Java Released

Java introduces its compile-then-interpret model with strict type checking. The compiler catches many errors early, and the JVM provides detailed runtime exception messages.

2004 • Java 5 Adds Generics

Generics allow the compiler to catch type-mismatch errors in collections that previously would only surface at runtime, shifting more bugs to compile time.

2010s • Modern IDE Integration

Tools like IntelliJ IDEA and Eclipse show compilation errors in real time as you type, making the compile-time safety net even more immediate and useful.

The central question this lesson addresses is straightforward but essential: when you see an error in Java, how do you determine whether it happened at compile time or at runtime, and what does that distinction tell you about how to fix it?

SECTION 2

Core Principles & Definitions

Before you can fix bugs efficiently, you need a clear mental model of when each type of error strikes. A compilation error (also called a compile-time error or syntax error) is detected by the javac compiler before your program ever runs. A runtime error (often called a runtime exception) occurs while the program is executing—the code compiled successfully, but something goes wrong during actual operation. These two categories require different diagnostic strategies.

1

Compilation Errors

Caught by javac before execution begins. These include syntax mistakes, undeclared variables, type mismatches, and missing imports. Your program will not produce a .class file until every compilation error is fixed.
2

Runtime Exceptions

Occur while the program is running. The code is syntactically correct, but a logical condition causes a crash—like dividing by zero, accessing an array out of bounds, or calling a method on a null reference. The JVM throws an exception object describing the problem.
3

Logic Errors (Bonus Category)

The program compiles and runs without crashing, but produces wrong results. For example, using + instead of − in a formula. These are the hardest to find because neither the compiler nor the JVM flags them automatically.
4

The Compiler as Gatekeeper

Java's compiler enforces strict rules about types, syntax, and structure. This is intentional—catching errors at compile time is cheaper and faster than discovering them after deployment. The stricter the compiler, the fewer surprises at runtime.
✦

KEY TAKEAWAY

Think of the Java compiler like a spell-checker for an essay. It catches misspellings and grammar mistakes (compilation errors) before you submit. But it cannot tell you if your essay argues the wrong point—that's a runtime or logic error. The spell-checker runs before anyone reads the essay; the reader discovers content problems during the reading.

SECTION 3

Visual Explanation — The Java Execution Pipeline

Java Execution Pipeline — Where Errors OccurMain.javaSource Codejavac (Compiler)Checks syntax, types, importsProduces bytecode (.class)successMain.classBytecodeJVM (Runtime)Executes bytecodeManages memory, threads✓ Program Output⚠ COMPILATION ERRORMissing semicolons, wrong types,undeclared variables, bad importsNO .class file producedfailure⚠ RUNTIME EXCEPTIONNullPointerException,ArrayIndexOutOfBoundsException,ArithmeticException (÷ by 0)crash
This diagram shows the two-phase pipeline. Your source code (Main.java) enters the compiler. If a compilation error is found, no .class file is created and the process stops. If compilation succeeds, the JVM runs the bytecode—but a runtime exception can still crash the program mid-execution.

Notice the two distinct failure points in the diagram. The compiler acts as a strict gatekeeper: if your code has syntax errors, type mismatches, or missing declarations, the compiler refuses to produce a .class file. You must fix every compilation error before you can even attempt to run the program. Runtime exceptions, on the other hand, only appear when the program is actually executing. The code looked fine to the compiler, but a specific data condition (like an empty list or a user typing text instead of a number) causes the JVM to halt.

SECTION 4

How Compilation & Runtime Errors Work

The Compilation Phase

When you run javac Main.java, the compiler performs several checks in sequence. First, it performs lexical analysis, breaking your code into tokens (keywords, identifiers, operators). Next comes syntactic analysis, which verifies that those tokens are arranged according to Java's grammar rules. Finally, semantic analysis checks that variable types match, methods exist, and all identifiers have been declared. If any check fails, the compiler outputs an error message with a file name, line number, and description.

ℹ

Common Compilation Errors

Missing semicolon → error: ';' expected | Incompatible types → error: incompatible types: String cannot be converted to int | Undeclared variable → error: cannot find symbol | Missing return statement → error: missing return statement

The Runtime Phase

Once compilation succeeds, you run java Main and the JVM begins executing your bytecode. Runtime errors happen when the program encounters a situation the compiler could not predict. For instance, the compiler sees int result = a / b; and confirms that both a and b are integers—that's valid syntax and valid types. But if b happens to be 0 when the line executes, the JVM throws an ArithmeticException. The compiler cannot know at compile time what value b will hold.

⚠

Common Runtime Exceptions

NullPointerException — calling a method on a null reference | ArrayIndexOutOfBoundsException — accessing an array index that doesn't exist | ArithmeticException — integer division by zero | StackOverflowError — infinite recursion exhausts the call stack

Reading an Error Message

Both types of errors give you clues, but they look different. A compilation error from javac typically shows the file name, line number, a caret (^) pointing to the exact character, and a description. A runtime exception prints a stack trace—a list of method calls that shows exactly where the crash occurred and the chain of calls that led to it. Learning to read these messages is the single most important debugging skill in Java.

SECTION 5

Classifying Common Java Errors

Now that you understand the two phases, let's classify the most common Java errors into their proper categories. The table below organizes errors you will encounter frequently, showing the error message, what causes it, and whether it strikes at compile time or runtime.

Error Classification MapCOMPILE-TIME ERRORSCaught by javac before executionSyntax ErrorMissing ; { } or misspelled keywordType MismatchAssigning String to int variableCannot Find SymbolVariable not declared or method misspelledMissing Return StatementMethod declares return type but has no returnUnreported ExceptionChecked exception not caught or declaredRUNTIME EXCEPTIONSThrown by JVM during executionNullPointerExceptionCalling method on null referenceArrayIndexOutOfBoundsIndex ≥ array length or negative indexArithmeticExceptionInteger division by zeroClassCastExceptionInvalid type cast at runtimeStackOverflowErrorInfinite recursion exceeds call stack
The left column shows errors that prevent compilation entirely. The right column shows exceptions that crash a running program. Notice that compile-time errors are about code structure and rules, while runtime exceptions are about data and program state.

A helpful rule of thumb: compile-time errors are about breaking Java's grammar and type rules, while runtime exceptions are about data-dependent situations the compiler cannot predict. If you accidentally write System.out.prinln (missing a 't'), the compiler catches it immediately because that method doesn't exist. But if you write names[i] where i could be any number entered by the user, the compiler has no way to know whether that index will be valid—only the running program can discover that.

SECTION 6

Worked Example — Diagnosing and Fixing Errors

Let's walk through a realistic piece of Java code that contains both a compilation error and a runtime error. We will identify each one, explain why it occurs, and fix it step by step.

Debugging a Student Grade Calculator

  1. 1

    Step 1 — Read the Original Code

    Consider the following program that attempts to calculate the average of student grades stored in an array: public class GradeCalc { public static void main(String[] args) { int[] grades = {90, 85, 78, 92, 88}; int sum = 0; for (int i = 0; i <= grades.length; i++) { sum = sum + grades[i] } double average = sum / grades.length; System.out.println("Average: " + average); } } This code has at least two bugs. Let's find them.
  2. 2

    Step 2 — Attempt Compilation

    When we run javac GradeCalc.java, the compiler reports: GradeCalc.java:6: error: ';' expected sum = sum + grades[i] ^ 1 error The caret (^) points to the end of line 6 where a semicolon is missing. This is a compilation error—a syntax violation that prevents the compiler from generating any bytecode.
    Fix: Add a semicolon at the end of line 6 → sum = sum + grades[i];
  3. 3

    Step 3 — Recompile Successfully

    After adding the semicolon, we run javac GradeCalc.java again. This time, compilation succeeds and a GradeCalc.class file is produced. The compiler found no further issues with the code's syntax or types.
    Compilation succeeds → GradeCalc.class is created.
  4. 4

    Step 4 — Run the Program and Encounter a Runtime Error

    We run java GradeCalc and see: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5 at GradeCalc.main(GradeCalc.java:5) This is a runtime exception. The loop condition is i <= grades.length, which allows i to reach 5. But the array indices go from 0 to 4 (length − 1). The compiler didn't catch this because the types and syntax were correct—it's a logic/data problem that only manifests during execution.
    Fix: Change <= to < → for (int i = 0; i < grades.length; i++)
  5. 5

    Step 5 — Verify the Fix

    After fixing the loop condition, we recompile and run again. The program outputs Average: 86.0. Note: integer division gives us 86.0 instead of 86.6. This is actually a logic error (the third category). To get the precise average, we would cast to double: double average = (double) sum / grades.length;. This example demonstrates all three error types in one program.
    Three bugs found: compilation error (missing ;), runtime exception (off-by-one in loop), and logic error (integer division).

SECTION 7

Compilation Errors vs. Runtime Exceptions — Side by Side

FeatureCompilation ErrorRuntime Exception
When detectedBefore the program runs (during javac)While the program is executing (during java)
CauseViolated Java syntax rules, type rules, or structural requirementsInvalid data conditions like null references, out-of-range indices, or division by zero
.class file produced?No — must fix all errors firstYes — compilation succeeded
Error message sourceThe compiler (javac) with line/column indicatorsThe JVM, printed as a stack trace with exception name and location
Can be prevented?Always — follow Java syntax and type rules correctlyOften — use input validation, null checks, and bounds checking
Example fix strategyRead the compiler message, go to the indicated line, fix the syntax or type issueRead the stack trace, identify the exception type, add defensive code (if-checks, try-catch)
Difficulty to fixUsually straightforward — the compiler tells you exactly where and whatCan be harder — you must understand what data caused the crash

Key differences between compilation errors and runtime exceptions in Java

✦

KEY TAKEAWAY

Think of compilation errors like a car that won't start because you put diesel in a gasoline engine—the car never leaves the driveway. Runtime errors are like a car that starts fine but gets a flat tire on the highway—it was working, but something in the environment caused a problem. In both cases, the car needs fixing, but the diagnostic approach is completely different.

SECTION 8

Connection to Advanced Error Handling

As you progress in Java, you will encounter a more nuanced picture of error handling. Java has a formal hierarchy of error classes rooted in java.lang.Throwable. This hierarchy splits into checked exceptions (which the compiler forces you to handle) and unchecked exceptions (which are the runtime exceptions we've discussed). Understanding this distinction bridges the gap between what you've learned today and more advanced topics like custom exception classes and the try-catch-finally mechanism.

ConceptWhat You Know NowWhat Comes Next
Error detectionCompile-time vs. runtime — two distinct phasesChecked vs. unchecked exceptions — the compiler can enforce handling of some runtime errors
Handling errorsFix compiler errors; trace runtime exceptions via stack tracesUse try-catch blocks to gracefully recover from exceptions without crashing
PreventionWrite correct syntax; validate data manuallyUse unit testing frameworks like JUnit to catch bugs before deployment
Custom errorsRecognize built-in exception typesCreate your own exception classes by extending Exception or RuntimeException

How today's concepts connect to advanced Java error handling

The key insight to carry forward is that Java's design philosophy is to catch as many problems as possible at compile time. Features like generics, strong typing, and checked exceptions all reflect this philosophy. As you learn frameworks and write larger programs, you'll appreciate that a compiler error—while sometimes annoying—is actually your best friend. It catches the problem immediately, tells you exactly where it is, and costs you seconds to fix. A runtime error in a deployed application, by contrast, might cost hours of debugging and affect real users.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

A student writes a Java program and runs javac MyApp.java. The terminal displays error: incompatible types: String cannot be converted to int on line 7. Is this a compilation error or a runtime exception? Explain how you can tell, and describe what the student should do to fix it.
Show answer
This is a compilation error. You can tell because the message came from javac (the compiler), not from running the program with java. The error message includes a file name and line number without a stack trace—this is the hallmark of a compile-time error. The student is trying to assign a String value to an int variable on line 7. To fix it, they should either change the variable type to String, or use Integer.parseInt() to convert the String to an int.

Question 2: BASIC CALCULATION

Look at this code snippet: String name = "Alice"; int x = name; Will this cause a compilation error or a runtime exception? Identify the specific error message the compiler would produce.
Show answer
This causes a compilation error. The compiler will report: error: incompatible types: String cannot be converted to int. Java is a statically typed language, meaning the compiler checks that all assignments are type-compatible before producing bytecode. Since String and int are completely different types with no automatic conversion, the compiler rejects this code. No .class file is produced.

Question 3: INTERMEDIATE

The following code compiles successfully. What will happen when it runs, and why didn't the compiler catch the problem? public class Test { public static void main(String[] args) { String[] colors = {"red", "green", "blue"}; for (int i = 0; i <= 3; i++) { System.out.println(colors[i].toUpperCase()); } } }
Show answer
This code will print "RED", "GREEN", and "BLUE" successfully, but then throw an ArrayIndexOutOfBoundsException when i reaches 3. The array has indices 0, 1, and 2, but the loop condition i <= 3 allows i to be 3. The compiler didn't catch this because the syntax and types are perfectly valid—colors[i] is a legitimate expression where i is an int. The compiler cannot evaluate what value i will hold during execution. The fix is to change <= 3 to < colors.length.

Question 4: APPLIED

You're building a program that reads a student's test score from user input and calculates a percentage. A friend gives you this code and says "it won't work." Classify each error as compilation or runtime, and fix both. import java.util.Scanner; public class ScoreCalc { public static void main(String[] args) { Scanner sc = new Scanner(System.in) System.out.println("Enter score: "); int score = sc.nextInt(); double percent = score / 0; System.out.println("Percent: " + percent); } }
Show answer
There are two errors. First, line 4 is missing a semicolon after new Scanner(System.in)—this is a compilation error that prevents the program from compiling at all. After adding the semicolon and recompiling, the program will run but crash on score / 0 with an ArithmeticException: / by zero—this is a runtime exception. The compiler sees integer division as valid syntax and valid types; it does not evaluate that the divisor is literally 0. The fix is to replace 0 with the total possible score, e.g., double percent = (double) score / totalPoints * 100;.

Question 5: CRITICAL THINKING

Java could theoretically be designed so that the compiler checks whether array accesses are always within bounds (some languages do this). Why do you think Java's designers chose not to make out-of-bounds array access a compilation error? Discuss at least two reasons, considering both the limits of compile-time analysis and the impact on programmer flexibility.
Show answer
There are several reasons. First, the compiler would need to predict the value of every index at every possible execution path, which is computationally impossible in the general case (this relates to the Halting Problem in computer science). When the index comes from user input, a file, or a complex calculation, the compiler simply cannot know its value. Second, enforcing bounds checking at compile time would be extremely restrictive—many perfectly safe array accesses would be flagged as errors because the compiler couldn't prove they were safe, forcing programmers to write extra code to satisfy the compiler even when the logic is clearly correct. Java strikes a balance: the compiler catches what it can (types, syntax), and the JVM enforces runtime safety by throwing exceptions for out-of-bounds access rather than allowing memory corruption.

SUMMARY

Lesson Summary

Summary

Java errors fall into two main categories based on when they occur. Compilation errors are caught by the javac compiler before your program runs—they include syntax mistakes, type mismatches, undeclared variables, and missing imports. No .class file is produced until every compilation error is resolved. Runtime exceptions occur during execution when the program encounters data conditions the compiler could not predict, such as null references, array index violations, and division by zero.

To debug effectively, start by reading the error message carefully. Compilation errors come from javac with a file name, line number, and caret pointing to the problem. Runtime exceptions appear as stack traces showing the exception type and the chain of method calls that led to the crash. Remember: compilation errors are about code structure and rules, while runtime exceptions are about data and program state. Mastering this distinction is the foundation of all debugging in Java.

Varsity Tutors • Java • Compilation vs. Runtime Errors