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
1972 • C Language Created
1995 • Java Released
2004 • Java 5 Adds Generics
2010s • Modern IDE Integration
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.
Compilation Errors
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.Runtime Exceptions
null reference. The JVM throws an exception object describing the problem.Logic Errors (Bonus Category)
The Compiler as Gatekeeper
KEY TAKEAWAY
SECTION 3
Visual Explanation — The Java Execution Pipeline
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
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 statementThe 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 stackReading 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.
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
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
Step 2 — Attempt Compilation
When we runjavac GradeCalc.java, the compiler reports:GradeCalc.java:6: error: ';' expected sum = sum + grades[i] ^ 1 errorThe 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
Step 3 — Recompile Successfully
After adding the semicolon, we runjavac GradeCalc.javaagain. This time, compilation succeeds and aGradeCalc.classfile is produced. The compiler found no further issues with the code's syntax or types.Compilation succeeds →GradeCalc.classis created. - 4
Step 4 — Run the Program and Encounter a Runtime Error
We runjava GradeCalcand 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 isi <= grades.length, which allowsito 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
Step 5 — Verify the Fix
After fixing the loop condition, we recompile and run again. The program outputsAverage: 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
| Feature | Compilation Error | Runtime Exception |
|---|---|---|
| When detected | Before the program runs (during javac) | While the program is executing (during java) |
| Cause | Violated Java syntax rules, type rules, or structural requirements | Invalid data conditions like null references, out-of-range indices, or division by zero |
| .class file produced? | No — must fix all errors first | Yes — compilation succeeded |
| Error message source | The compiler (javac) with line/column indicators | The JVM, printed as a stack trace with exception name and location |
| Can be prevented? | Always — follow Java syntax and type rules correctly | Often — use input validation, null checks, and bounds checking |
| Example fix strategy | Read the compiler message, go to the indicated line, fix the syntax or type issue | Read the stack trace, identify the exception type, add defensive code (if-checks, try-catch) |
| Difficulty to fix | Usually straightforward — the compiler tells you exactly where and what | Can be harder — you must understand what data caused the crash |
Key differences between compilation errors and runtime exceptions in Java
KEY TAKEAWAY
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.
| Concept | What You Know Now | What Comes Next |
|---|---|---|
| Error detection | Compile-time vs. runtime — two distinct phases | Checked vs. unchecked exceptions — the compiler can enforce handling of some runtime errors |
| Handling errors | Fix compiler errors; trace runtime exceptions via stack traces | Use try-catch blocks to gracefully recover from exceptions without crashing |
| Prevention | Write correct syntax; validate data manually | Use unit testing frameworks like JUnit to catch bugs before deployment |
| Custom errors | Recognize built-in exception types | Create 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
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
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
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
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
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
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
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
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
Show answer
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.