For this example we will use the debugger integrated in IntelliJ.
In short a debugger allows you to stop the execution of your program once it hits a certain instruction/line of code, by setting a so called "breakpoint". When stopped the debugger then provides you with:
- a detailed view of the state of your program
- the ability to navigate the state of your program
- the ability to execute functions and inspect their result
Breakpoints generally can be set by double clicking right next to the line number.
Breakpoints can also be conditional. Lets say you are iterating through a list of objects and performing actions on their fields. This list has a size of multiple thousands and at some point it fails with a NullPointerException. By simply setting the condition of the breakpoint (instance.field == null), the program will stop just before the crash, letting you examine everything.
You can type whatever you want in the condition field, you can even call methods and access members. All that matters is that in the end it results in a boolean
Manually executing functions
A really helpful feature is IntelliJ's "evaluate expression", which allows you to dynamically execute code from the scope of your breakpoint.
As soon as you have hit a breakpoint open the menu like so:
Now you can execute any piece of code and have the return value displayed and even examine it: