Praca z debuggerem w Eclipse
Do czego służy debugger?
W trakcie tworzenia kodu programistycznego zdarza się, że popełnimy błąd w kodzie, który potem skutkuje problemem z kompilacją lub nieprawidłowym działaniem programu.
Zazwyczaj środowisko programistyczne na bieżąco monitoruje składnię naszego programu i zaznacza nam linijki z błędem lub ostrzeżeniem.
Jednakże, częstymi błędami jakie przytrafiają się w trakcie programowania obiektowego jest na przykład odwoływanie się do nieistniejącego obiektu, lub indeksu w tablicy.
Tego typu problemy możemy w wygodny sposób odszukać i poprawić stosując debugger.
Debugger jest opcją uruchomienia naszego kodu w trybie śledzenia jego działania krok po kroku. Dzięki niemu możemy prześledzić dokładnie które linijki kodu są wykonywane i w jakiej kolejności.
Aby to zademonstrować stwórzmy dwie klasy: Punkt.java i Main.java.
Punkt.java
public class Punkt { double x; double y; double z; public Punkt() // konstruktor { x = 0; y = 0; z = 0; } public double getX() { return x; } public void setX(double x) { this.x = x; } public double getY() { return y; } public void setY(double y) { this.y = y; } public double getZ() { return z; } public void setZ(double z) { this.z = z; } public double getR() { return Math.sqrt(x * x + y * y + z * z ); } }
Main.java
import java.util.Random; public class Main { static void fillPoint(Punkt p) // metoda na obiekcie klasy Punkt { Random rand = new Random(); p.setX(rand.nextDouble()); p.setY(rand.nextDouble()); } public static void main(String[] args) { System.out.println("Program obiczajacy promienie"); Punkt [] tablica = new Punkt[4]; for (int i = 0; i < 4; i++) { tablica[i] = new Punkt(); } for(int i = 0; i < 5; i++) { fillPoint(tablica[i]); System.out.println("Promien nr " + i + " = " + tablica[i].getR()); } } }
Przedstawiony kod nie zawiera w sobie błędów składni, zatem Eclipse nie będzie nam zgłaszał żadnych problemów. Jak widać program tworzy w pętli tablicę 4-elementową, którą wypełnia wartościami losowymi.
Następnie program zwraca do konsoli wartość promienia r wyliczoną na podstawie wartości współrzędnych punktów.
Jednakże, po skompilowaniu i uruchomieniu programu wzracane jest kilka wyników i następujący błąd:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 4
at debug1.Main.main(Main.java:25)
Otrzymujemy wyjątek wraz z numerem linii wskazujący że odwołujemy się w pewnej iteracji do lementu tablica[4] czego oczywiście zrobić nie może - bo taki element nie istnieje.
Należy zatem przeprowadzić procedurę debugowania naszego kodu aby znaleźć błędną instrukcję.
Procedura debuggowania kodu programu
Nasz program uruchamiamy w menu Run\Debug lub skrótem klawiszowym F11. Można również uruchomić debugger za pomoca ikony robaka w pasku narzędziowym.
Eclipse poprosi nas o potwierdzenie zmiany perspektywy w oknie. Potwierdzamy nasz wybór i zauważamy zmianę układu paneli w oknie Eclipse'a.
Mamy teraz możliwość dodania punktów nawigacyjnych (breakpoints) oraz monitorujących jakąś zmienną (watchpoints).
Breakpointy możemy ustawić klikając po lewej stronie okna z kodem i wybierając Toggle Breakpoint.
Przegląd naszego kodu mozemy dokonać za pomocą następujących klawiszy: lub też za pomocą klawiszy funkcyjnych na klawiaturze.
- Step Into (F5) - wykonuje aktualną linię kodu i przechodzi do następnej. Jeśli wyróżniona linia jest metodą, to debugger przechodzi do ciała metody. Jest to najdokładniejsza metoda przeglądania programu.
- Step Over (F6) - wykonuje kolejną instrukcję ale bez wchodzenia debuggerem do metody. Używane jest kiedy mamy pewność że dana metoda jest poprawnie napisana i nie ma potrzeby analizowania jej kodu.
- Step Return (F7) - kończy wykonanie metody w jednym kroku i wraca do metody wywołującej. Używamy kiedy chcemy pominąć dalsze sprawdzanie instrukcji danej metody i przejść do jej końca.
- Resume (F8) - skok do następnego punktu nawigacyjnego.
Celem zademonstrowania działania debuggera na naszym przykładzie wykonajmy następujące czynności:
- postawmy pierwszy breakpoint w linii 3 klasy Punkt. (linijka kodu: double x)
- Uruchamiamy debuggowanie (ikona robaka, F11, lub Run\Debug) i program zatrzymuje się w konstruktorze klasy Punkt.
Program wykonał się do pierwszego breakpointa / watchpointa. W naszym przypadku program będzie się zatrzymywał zawsze gdy użyta zostanie zmienna x.
Pierwszy raz odwołanie do zmiennej x następuje w konstruktorze klasy Punkt, zatem tam jest pierwsze zatrzymanie programu. - Wciśnięcie w tym etapie klawisza F5 (Step Into) wykona kolejną linię kodu ( double y). Wciśnięcie F7 (Step Return) spowoduje wykonanie dalszych instrukcji automatycznie i wyjście z metody (w tym przypadku z konstruktora dla obiektu tablica[0]).
- Wciskamy ponownie F5 (Step Into) i ponownie wchodzimy do konstruktora, ale tym razem dla obiektu tablica[1].
Kiedy znajdziemy się w konstruktorze klasy Punkt dla obiektu tablica[1] wciśnięcie F5 (Step Into) spowoduje przejście do kodu klasy Object.class. Wynika to z faktu, że klasa Punkt dziedziczy po klasie Object, zatem konstruktor klasy nadrzędnej jest wywoływany na początku klasy potomnej. - Odznaczmy teraz nasz breakpoint z linii 3 w prawym górnym oknie w zakładce Breakpoints i wstawmy nowy breakpoint w linii 38 (this.z = z)
- Obserwujemy zmienną i w oknie Variables i wciskamy F8 (Resume) aby przejść do dalszych instrukcji naszego kodu. W pewnym momencie zmienna i osiąga wartość 4 i następny krok powoduje błąd.
Widzimy teraz że błąd w naszym kodzie wiąże się ze źle zdefiniowaną drugą pętla for. Odwołuje się ona do obiektu o indeksie 4, który nie został stworzony. - Wychodzimy z trybu debuggowania wciskając ikonę z literką J (druga od prawej w pasku narzędziowym).