Chapter 2: Java Prerequisites for DSA
2.09Beginner8 topics14 min

Debugging and tools

How to find and prevent bugs: assertions, logging versus printing, the IDE debugger and breakpoints, reading stack traces, unit tests, building and running from the command line, and Java version compatibility.

Learning goals

  • Use assertions and tests to catch bugs early
  • Step through code with a debugger
  • Read a stack trace from the bottom up
  • Compile and run from the command line

Prerequisites

Java language essentials
2.09.1

Assertions

assert condition : message; checks an assumption and throws AssertionError when it is false. Assertions are disabled by default; enable them with java -ea. Use them for internal invariants, never for validating user input.

Because they may be off, an assertion must not have side effects.

Java
1static int pop(int[] stack, int size) {
2 assert size > 0 : "pop from empty stack";
3 return stack[size - 1];
4}
5// run: java -ea Main
2.09.2

Logging vs printing

Temporary prints are quick, but they clutter output and are easy to forget. java.util.logging or a logging library gives levels (INFO, WARNING, SEVERE), timestamps and switches. In contests print debug lines to System.err and remove them before submitting.

Never log inside a hot loop; building the message costs time even if it is discarded.

Java
1Logger log = Logger.getLogger("algo");
2log.info("start");
3System.err.println("i=" + 3 + " sum=" + 12); // debugging without touching stdout
2.09.3

IDE debugger

A debugger pauses the program and lets you inspect variables, step line by line and evaluate expressions. In VS Code or IntelliJ start a run in debug mode, then use Step Over (next line), Step Into (enter a call), Step Out (finish the method) and Continue (run to the next breakpoint).

Watching a variable change step by step is the same skill as a tracing table, done by the machine.

2.09.4

Breakpoints

A breakpoint stops execution at a line. A conditional breakpoint stops only when an expression is true (i == 9999), a hit-count breakpoint after N passes, and an exception breakpoint when a given exception is thrown. These let you skip thousands of uninteresting iterations.

Place the breakpoint just before the line you suspect, not at the start of the program.

2.09.5

Stack traces

A stack trace lists the call chain at the moment of an exception: the first line is the exception and message, the next is where it was thrown, then each caller. Read the top first, find the first line in your own code, and look at the values used on that line.

Typical messages: ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5 (off by one), NullPointerException (unset reference), StackOverflowError (missing base case).

Java
1Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
2 at Main.sum(Main.java:6) <- line to inspect first
3 at Main.main(Main.java:12) <- who called it
2.09.6

Unit tests

A unit test runs a small piece of code on known input and checks the result automatically. With JUnit 5, annotate methods with @Test and use assertEquals. Test the normal case, the edge cases and a regression for every bug you fix.

For algorithms, also compare against a brute-force version on random small inputs.

Java
1import org.junit.jupiter.api.Test;
2import static org.junit.jupiter.api.Assertions.*;
3
4class SearchTest {
5 @Test void findsMiddle() { assertEquals(2, Search.binarySearch(new int[]{1, 3, 5, 7}, 5)); }
6 @Test void missingReturnsMinusOne() { assertEquals(-1, Search.binarySearch(new int[]{1, 3}, 2)); }
7 @Test void emptyArray() { assertEquals(-1, Search.binarySearch(new int[0], 1)); }
8}
2.09.7

Build and run

Compile with javac Main.java and run with java Main. Since Java 11 you can run a single source file directly with java Main.java. Use -ea to enable assertions, -Xmx512m to cap the heap and -Xss to enlarge the thread stack for deep recursion.

Build tools (Maven, Gradle) automate compiling, testing and packaging for larger projects.

Java
1javac Main.java
2java -ea Main < input.txt
3java Main.java # compile and run in one step (Java 11+)
4java -Xss64m -Xmx256m Main # larger stack, bounded heap
2.09.8

Java version compatibility

Check the Java version with java -version. Language features arrive over time: var (10), switch expressions (14), text blocks (15), records (16), sealed classes (17) and pattern matching for instanceof (16). Code that uses a newer feature will not compile on an older JDK.

This course targets Java 17. If a judge or project uses an older version, avoid records and switch expressions, or check what the platform supports first.

FeatureJava version
var for locals10
Switch expressions14
Text blocks15
Records, pattern instanceof16
Sealed classes17
Long-term support releases8, 11, 17, 21

Practice ladder

Now try it yourself

Easy

  • Add an assertion to a method that expects a non-negative argument
  • Run a program with and without -ea

Medium

  • Read a stack trace for an off-by-one error and identify the faulty line
  • Write three JUnit tests for a max method, including an empty array

Advanced

  • Use a conditional breakpoint to find the first iteration where a running sum becomes negative

Key takeaways

  • Assertions and tests catch bugs close to their cause.
  • Read stack traces from the top, starting with your own code.
  • Know your JDK version; this course uses Java 17.