Debugging i Java
Når ens program ikke producerer det forventede resultat. Altså det kan godt kompileres men programkørslen giver et resultat, som ikke stemmer overens med det man forventer f.eks. ved beregninger eller logiske operationer, så kan debuggeren hjælpe.
Når vi har lavet noget kode og testkører det enten i en unit test eller bare i en main metode og ikke kan forstå hvorfor resultatet ikke er som forventet, kan vi prøve forskellige ting:
- Vi kan prøve at soute (System.out.println) værdierne af variablerne på forskellige steder i koden for at se, hvor det går galt. Det er en fin måde, hvis vi hurtigt vil bekræfte eller afkræfte en hypotese. F.eks. At vi på et bestemt sted i vores kode kommer ind i et
if statement
. - Vi kan lave unit tests, der sikrer 100% coverage af kode, hvor der kan opstå logiske eller matematiske fejl i runtime. Dvs. at vi i testen bruger input til metoden, der sikrer at vi er kommet ned i alle
if statement
grene ogfor loop
iterationer. - Vi kan bruge IntelliJ’s debugger til at sætte breakpoints og inspicere værdierne af variablerne på runtime. Dette giver os mulighed for at se, hvad der sker i koden trin for trin og finde fejl mere effektivt.
- Her er et konkret eksempel på en fejl, der kan opstå når vi kører vores kode. Her vil kørselen producere en
ArrayIndexOutOfBoundsException
.
public class SumArray {
public static void main(String[] args) {
int[] numbers = {3, 5, 7, 9, 11};
int sum = 0;
for (int i = 0; i <= numbers.length; i++) { // bug: should be <
sum += numbers[i];
}
System.out.println("Sum = " + sum);
}
}
Dette kan for det utrænede øje være svært at opdage, da fejlen ikke vil blive opdaget før programmet forsøger at tilgå et element uden for arrayets grænser. Ved at bruge debuggeren og sætte et breakpoint på linjen med
sum += numbers[i];
kan vi inspicere værdien afi
og se, at den overstiger arrayets grænser.Steps
- Programmet opfører sig uventet.
- Vi kigger på koden og identificerer, hvor det er mest sandsynligt at fejlen opstår. Dette vil ofte være inde i løkker, betingelser eller matematiske udtryk.
- Når vi har fundet linjen hvor vi tror fejlen opstår, kan vi sætte et breakpoint i IntelliJ
- Nu kan vi køre programmet i debug-tilstand. Dette giver os mulighed for at inspicere værdierne af variablerne på runtime og se, hvad der sker i koden trin for trin.
- Vi kan justere vores breakpoint eller tilføje nye breakpoints for at fokusere på specifikke områder af koden, indtil vi har fundet og løst fejlen.
- I Intellij kan vi bruge “Evaluate Expression” værktøjet (højreklik på et metodekald og vælg “Evaluate Expression”) til at teste og evaluere udtryk i den nuværende kontekst, mens vi er i debug-tilstand.
- Vi kan også bruge “Step Over” og “Step Into” funktionerne i debuggeren til at navigere gennem koden linje for linje og se, hvordan værdierne ændrer sig.
- At arbejde med en hypotese og sammenligne med det faktiske resultat, minder meget om den måde vi har arbejdet med TDD på. Vi kan her se hvordan de to tilgange supplerer hinanden.
- Når vi kører et program og vi får kastet en exception (Både checked og unchecked exceptions), så kan vi bruge metode stakken til at se, hvilke metoder der blev kaldt op til fejlen. Den bliver vist i output vinduet i “run” fanen.
- Her kan vi se rækkefølgen af metodekald, der førte til fejlen.
- Når vi debugger følger vi metodestakken fra det første kald til main videre til de metoder, der blev kaldt derfra.
- Med step over og step into funktionerne kan vi navigere gennem koden og se, hvordan værdierne ændrer sig i hver metode.
- Step over betyder, at vi springer over metode definitionen og går direkte til næste linje i den nuværende metode, mens
- step into betyder, at vi går ind i metodedefinitionen og ser, hvad der sker der.
- I IntelliJ kan vi også overvåge værdierne af variablerne i realtid, mens vi debugger. Dette kan gøres ved at højreklikke på en variabel og vælge “Watch” for at tilføje den til overvågningsvinduet.
- Dette giver os mulighed for at se, hvordan værdierne ændrer sig, mens vi træder gennem koden, og kan hjælpe os med at identificere problemer hurtigere.
- Mens vi debugger, kan vi højreklikke på en variabel eller et udtryk og vælge “Evaluate Expression” for at teste og evaluere det i den nuværende kontekst uden at vi stopper debuggeren.
- I debug vinduet er der en række knapper (rerun, stop, … etc) Yderst til højre er tre punktummer der giver adgang til flere kommandoer, herunder muligheden for at nulstille den aktuelle frame. Det betyder at vi kan fjerne det sidste metode kald fra metodestakken og nulstille alle variabler til deres oprindelige værdier. På den måde kan vi gå lidt tilbage i debugging processen uden at skulle starte helt forfra.