Lekce 4 - IntelliJ IDEA - Debugging
V minulé lekci, IntelliJ IDEA - Scopes, Live Templates, Databáze, Pluginy, jsme si ukázali scopes, live templates, SQL Explorer a pluginy.
V dnešním Java tutoriálu si vysvětlíme, jak debugovat Java aplikace za pomocí IDE IntelliJ IDEA. Vše si vyzkoušíme na jednoduchém příkladu.
Debugging
Debugging je proces odstranění chyb v programu, které obvykle nejsou na první pohled vidět. Někdy se o něm hovoří také jako o "ladění" programu.
Proč a kdy debugovat?
Chyby v programu se většinou projevují tím, že je v nějaké proměnné nesprávná hodnota, která pak zapříčiní nefunkčnost programu. Určitě jste se již dostali do situace, když váš program nefungoval správně. Možná jste prováděli nějaké zdlouhavé kontrolní výpisy různých proměnných, abyste zjistili, kde je ještě vše správně a kde jsou již nesprávné hodnoty.
Chybu lze ovšem obvykle najít mnohem snadněji pomocí debugování. Program obvykle spustíme ve speciálním režimu, kde máme dovoleny určité "cheaty", jako např. program pozastavovat a nahlížet do obsahu proměnných. Právě to se dnes naučíme.
Není ovšem pravda, že debugger používáme jen když program nefunguje! Naopak, používáme jej co nejvíce to jde a to i třeba pro kontrolu správnosti na kritických místech.
Příklad - Krokování cyklu
Mějme jednoduchý cyklus, ve kterém budeme vypisovat čísla od
0
do 9
:
package onlineapp;
class Program
{
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println("Číslo: " + i);
}
}
}
Jak si můžete zkusit, program funguje korektně. Přesto si jej zkusíme oddebugovat, než se pustíme na nefunkční kód.
Breakpointy
Breakpoint je základní stavební kámen při debugování. Je to "bod" označující řádek, na kterém se program zastaví. Nám, jako programátorům, se v tu chvíli zobrazí kompletní kontext programu, tedy např. co je v tu chvíli kde uloženo. Protože program stojí, můžeme si vše snadno zkontrolovat.
Přidání breakpointu
Nyní se naučíme vkládat breakpointy a zastavovat na nich program. V IntelliJ se breakpoint na daný řádek vkládá kliknutím do prostoru vlevo vedle čísla řádku. Breakpoint pak poznáme podle červeného kolečka. Vložme tedy breakpoint na řádek, kde se vypisuje číslo do konzole:
Debug režim
Breakpoint máme přidaný. Když však program nyní spustíme, nic zvláštního se nestane. Aby se program opravdu zastavil, je potřeba ho spustit ve speciálním debugovacím režimu. K tomu slouží speciální tlačítko vedle tlačítka "Play":
Klikneme na tlačítko pro debug. Program se spustí a až narazí na náš breakpoint, tak se zastaví:
Červený puntík označující breakpoint se změnil na zelený. To znamená, že breakpoint je dosažitelný.
Záložka Debug
Otevřela se nová záložka nazvaná Debug, která obsahuje velké množství informací. Pojďme si popsat, co vidíme:
- část ohraničená na obrázku výše zeleně obsahuje seznam všech aktuálně viditelných proměnných a jejich hodnoty; hodnoty lze změnit,
- oranžově ohraničená část obsahuje zásobník volání všech funkcí, které se zavolaly před breakpointem
Všimněte si, že IntelliJ se snaží vkládat hodnoty proměnných přímo do editoru. V našem případě vidíme přímo výpis hodnot pole.
Dále jsou na obrázku zvýrazněny dvě části ohraničeny červeně.
Ovládání programu a breakpointů
V levé ohraničené červené části se nachází tlačítka pro kontrolu ovládání programu a breakpointů:
- rerun 'Main' Ctrl+F5 - Ukončí aktuální program a znovu ho spustí,
- modify run configuration - Zobrazí nastavení pro debug,
- resume program F9 - Uvolní program z breakpointu a nechá program běžet a případně se zastaví o další breakpoint,
- pause program - Pozastaví vykonávání vybraného vlákna - funguje podobně jako breakpoint, pouze se nezobrazí žádné proměnné,
- stop 'Main' Ctrl+F2 - Ukončí aktuální program,
- view breakpoints Ctrl+Shift+F8 - Zobrazí seznam všech breakpointů,
- mute breakpoints - Potlačí všechny breakpointy; Když program narazí na breakpoint v debug modu, breakpoint bude ignorován.
Krokování
Ve druhé červeně ohraničené části se nachází tlačítka pro krokování programu. Pomocí krokování můžeme nechat program pokročit na další řádek a zas se zastavit:
- step over F8 (krok přes) - Provedeme další krok přes danou řádku s breakpointem, na které program aktuálně stojí. Pokud se na řádce volá funkce, necháme ji jen vykonat a pak přejdeme na další řádek
- step into F7 (krok do) - Provedeme další krok "do" dané řádky. Dělá to samé co Step over až na rozdíl, že pokud se na řádku volá funkce, přejdeme na první řádek ve funkci
- force step into Alt + Shift + F7 (vynucený krok do) - Vynutí přechod do funkce na řádku i v případě, že se nejedná o naši metodu.
- step out Shift + F8 (krok ven) - Pomocí kroku ven vystoupíme z aktuálně prováděné funkce
My se nacházíme v cyklu, kde pouze vypisujeme jednu hodnotu. Když se podíváme do výpisu konzole, zatím neuvidíme žádný výpis (kromě možných hlášek od IDE).
Stiskem tlačítka krok přes se vypíše do konzole první
číslo a my se v programu dostaneme na řádek s for
cyklem. Když
tlačítko stiskneme znovu, uvidíme, že se inkrementovala pomocná proměnná
i
z hodnoty 0
na hodnotu 1
. Tímto
způsobem si můžeme odkrokovat celý cyklus:
Příklad - Debugování programu
Nakonec si dáme příklad opravdu k procvičení debugování. Máme dva vnořené cykly, které by měly vypsat malou násobilku:
package onlineapp;
class Program
{
public static void main(String[] args) {
for (int j = 1; j <= 10; j++) {
for (int i = 1; j <= 10; j++) {
System.out.print(i * j + "\t");
}
System.out.println();
}
}
}
Výsledek by měl vypadat takto:
Konzolová aplikace
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42 48 54 60
7 14 21 28 35 42 49 56 63 70
8 16 24 32 40 48 56 64 72 80
9 18 27 36 45 54 63 72 81 90
10 20 30 40 50 60 70 80 90 100
Problém ale je, že se vypíší pouze čísla od 1
do
10
:
Konzolová aplikace
1 2 3 4 5 6 7 8 9 10
Chybu budeme samozřejmě chtít najít a opravit za pomoci breakpointů a krokování. I když uvidíte chybu na první pohled, neotálejte a také si to odkrokujte! Ve složitějších aplikacích chyba snadno vidět nebude a debugování bude nutné.
Postup řešení
Očekáváme, že vnější cyklus se provede 10x, zatímco vnitřní cyklus
se provede celkem 100x. Vložíme breakpoint na řádek s
vnějším for
cyklem:
for (int j = 1; j <= 10; j++) {
A spustíme program v debug režimu. Bude nás zajímat, jak se proměnné
i
a j
zvyšují. Tlačítkem krok do
budeme postupně procházet přes oba cykly a pozorovat proměnné
i
a j
. Vnější cyklus pracuje s proměnnou
j
, která by se měla změnit pouze 10x. Vnitřní cyklus pracuje s
hodnotou i
, která by se měla při každé změně j
upravit 10x.
Postupným krokováním však zjistíme, že vnitřní cyklus inkrementuje
proměnnou j
namísto i
. Ve vnitřním cyklu upravíme
j++
na i++
a program opět spustíme v debug
režimu:
for (int i = 1; j <= 10; i++)
Krokujeme opět tlačítkem krok do. Sledujeme proměnnou
i
. Po čase začne nabývat neočekávaných hodnot, které se
neustále o jedničku zvyšují. Pokud teď necháte program běžet, začne se
vám počítač sekat. To je z důvodu, že ve vnitřním cyklu je podmínka,
která nikdy nebude splněna.
V cyklu for (int i = 1; j <= 10; i++)
máme proměnnou
i
, kterou už správně inkrementujeme, ale podmínka je špatná.
Proměnná i
se inkrementuje, dokud platí j <= 10
.
Tato podmínka nikdy nebude splněna, protože proměnná j
se
modifikuje pouze ve vnějším cyklu. Opravíme podmínku vnitřního cyklu z
j <= 10
na i <= 10
a program už spustíme bez
breakpointů:
package onlineapp;
class Program
{
public static void main(String[] args) {
for (int j = 1; j <= 10; j++) {
for (int i = 1; i <= 10; i++) {
System.out.print(i * j + "\t");
}
System.out.println();
}
}
}
Výsledkem již je očekávaná malá násobilka:
Konzolová aplikace
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42 48 54 60
7 14 21 28 35 42 49 56 63 70
8 16 24 32 40 48 56 64 72 80
9 18 27 36 45 54 63 72 81 90
10 20 30 40 50 60 70 80 90 100
V další lekci, IntelliJ IDEA - Pokročilý debugging, si rozšíříme znalosti debugování v IntelliJ IDEA.