EMZETT.
Login

Debugging

In short: Systematically tracking down and fixing errors in code — in Java usually with the IDE’s debugger (breakpoints, step-by-step execution, variable inspection).

In more detail: A breakpoint pauses the program at a specific line, so you can inspect the current state of all variables instead of guessing blindly. Stack traces for exceptions and targeted System.out.println output also help, even though the latter quickly hits its limits with more complex bugs.

In Depth

The systematic debugging workflow in an IDE (IntelliJ, Eclipse, VS Code): set a breakpoint by clicking the line number column, start the program in debug mode (not normal run mode), and as soon as the marked line is reached, execution pauses there automatically. After that:

  • Step Over: execute the next line without jumping into called methods
  • Step Into: jump into a called method to follow its execution
  • Step Out: let the current method run to completion and jump back to the caller
  • Variables window: shows the current values of all visible variables live
  • Watch expressions: define your own expressions that are re-evaluated at every pause (e.g. list.size() > 0)

Conditional breakpoints (right-click on the breakpoint) only trigger when a specific condition is met — e.g. only pause on the 50th loop iteration instead of every single one. A stack trace for an uncaught exception lists the exact method call path that led to the error, from top to bottom — the TOP line usually shows the actual cause of the error, while further down is the context of how you got there.

Before using a full-fledged debugger, many people (especially beginners) reach for targeted System.out.println() output to make program flow and variable values visible:

System.out.println("DEBUG: x = " + x + ", state = " + state);

This works for simple cases, but quickly becomes cluttered for more complex bugs (many outputs that eventually have to be removed again) and offers no way to pause program execution and interactively examine it — a real debugger is superior for that.

Logging instead of println for production code

For code that’s meant to stay in operation even after debugging, a logging library (e.g. SLF4J with Logback) is the better approach than System.out.println(): log levels (DEBUG, INFO, WARN, ERROR) can be turned on/off at runtime, output automatically lands in files with a timestamp instead of just the console, and debug output doesn’t have to be laboriously removed from the code again.

Rubber duck debugging

A well-known, non-technical debugging technique: explain the problem out loud (e.g. to a rubber duck on the desk) step by step. Merely being forced to formulate your own logic in complete sentences surprisingly often reveals the cause of the error, even before starting a debugger at all.

Common bug categories

Compiler errors (syntax errors, missing semicolons) are detected before execution even starts and are usually the easiest to fix. Runtime errors (exceptions like NullPointerException) only show up during execution, but at least provide a stack trace as a clue. Logic errors (the program runs without errors, but delivers a wrong result) are the hardest to find, because no error/exception points to the problem — here, targeted debugging with breakpoints at critical spots is usually unavoidable.

See also: Errors, Exceptions