← Back to Core Java Course | Chapter 14: Advanced Topics | Lesson 6 of 6

Java Debugging

Reading a Stack Trace

When an uncaught exception crashes a program, Java prints a stack trace: the exception type, its message, and the chain of method calls that led to it, listed innermost-first. The line numbers in each 'at ClassName.method(File.java:N)' entry point straight to the exact line that failed, and the cause is usually in the top-most frame belonging to your own code rather than a JDK internal class.

Example: Reading a Stack Trace

java
public class Main {
	static void level2() {
		throw new RuntimeException("Something broke");
	}
	static void level1() {
		level2();
	}
	public static void main(String[] args) {
		level1(); // uncaught: prints a stack trace, innermost call first
	}
}

Debugging with println

Sprinkling System.out.println() calls at key points in a method is a quick way to inspect variable values without setting up a debugger, especially for a one-off check. It's blunt and requires editing the source and rerunning, so it doesn't scale well for tracking down subtle bugs, but it remains the fastest first step for many developers.

Example: Debugging with println

java
public class Main {
	public static void main(String[] args) {
		int x = 5;
		System.out.println("x = " + x); // quick inspection, no debugger needed
		x = x * 2;
		System.out.println("x after doubling = " + x);
	}
}

Using a Debugger and Breakpoints

A real debugger lets you pause execution at a breakpoint and inspect every variable's live value without modifying the code, which println debugging can't do. Step Over runs the current line and moves to the next one in the same method, Step Into follows execution down into a called method, and Step Out finishes the current method early and returns you to its caller.

Example: Using a Debugger and Breakpoints

java
public class Main {
	public static void main(String[] args) {
		int total = 0;
		for (int i = 1; i <= 3; i++) {
			total += i; // a breakpoint here lets you inspect total live
		}
		System.out.println(total);
	}
}

Common IDE Debugger Features

Modern IDEs add conditional breakpoints (pause only when an expression is true, useful inside a loop that runs thousands of times), watch expressions that track a variable's value across the whole session, and an interactive evaluation console for testing an expression mid-pause without editing the source. These features turn debugging from guesswork into a controlled inspection process.

Example: Common IDE Debugger Features

java
public class Main {
	public static void main(String[] args) {
		for (int i = 0; i < 1000; i++) {
			if (i == 500) { // conditional breakpoint would pause only here
				System.out.println("Reached 500");
			}
		}
	}
}

Compile Errors vs Runtime Exceptions vs Logic Bugs

A compile-time error means the code never even runs (a missing semicolon, wrong type, unresolved symbol) and the compiler reports it before anything executes. A runtime exception means the code compiled fine but something went wrong while running (dividing by zero, a null reference) and Java reports it via a stack trace. A logic bug is the hardest of the three: the program compiles and runs without any error at all, it just silently produces the wrong answer, which is why deliberate debugging technique matters most here.

Example: Compile Errors vs Runtime Exceptions vs Logic Bugs

java
public class Main {
	public static void main(String[] args) {
		int[] arr = {1, 2, 3};
		try {
			System.out.println(arr[5]); // runtime exception, not a compile error
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("Runtime exception caught");
		}
		System.out.println(2 + 2); // if this printed 5, that'd be a logic bug
	}
}
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.