Czasami po uruchomieniu aplikacji pojawia się błąd, który wygląda następująco:
Exception in thread "main" java.lang.NullPointerException
at com.example.myproject.Book.getTitle(Book.java:16)
at com.example.myproject.Author.getBookTitles(Author.java:25)
at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
Ludzie nazywają to „śladem stosu”. Co to jest ślad stosu? Co może mi powiedzieć o błędzie występującym w moim programie?
O tym pytaniu - dość często widzę pytanie, w którym początkujący programista „dostaje błąd”, a oni po prostu wklejają swój ślad stosu i jakiś losowy blok kodu, nie rozumiejąc, co to jest ślad stosu i jak mogą go użyć. To pytanie jest przeznaczone dla początkujących programistów, którzy mogą potrzebować pomocy w zrozumieniu wartości śledzenia stosu.
java
debugging
stack-trace
Rob Hruska
źródło
źródło
Odpowiedzi:
Mówiąc najprościej, ślad stosu jest listą wywołań metod, które aplikacja znajdowała się w trakcie zgłaszania wyjątku.
Prosty przykład
Na przykładzie podanym w pytaniu możemy dokładnie określić, gdzie wyjątek został zgłoszony w aplikacji. Spójrzmy na ślad stosu:
Jest to bardzo prosty ślad stosu. Jeśli zaczniemy na początku listy „o…”, możemy stwierdzić, gdzie wystąpił nasz błąd. To, czego szukamy, to najlepsze wywołanie metody, które jest częścią naszej aplikacji. W tym przypadku jest to:
Aby to debugować, możemy otworzyć się
Book.java
i spojrzeć na linię16
, która jest:Oznaczałoby to, że coś (prawdopodobnie
title
) jestnull
w powyższym kodzie.Przykład z łańcuchem wyjątków
Czasami aplikacje przechwytują wyjątek i ponownie zgłaszają go jako przyczynę innego wyjątku. Zwykle wygląda to tak:
Może to dać ślad stosu, który wygląda następująco:
To, co różni się w tym, to „Spowodowane przez”. Czasami wyjątki zawierają wiele sekcji „Przyczyną”. W tym przypadku zwykle chcesz znaleźć „główną przyczynę”, która będzie jedną z najniższych sekcji „Spowodowanych przez” w śladzie stosu. W naszym przypadku jest to:
Ponownie, z tym wyjątkiem, że chcemy, aby spojrzeć na linii
22
zBook.java
, aby zobaczyć, co może spowodować, żeNullPointerException
tutaj.Bardziej zniechęcający przykład z kodem bibliotecznym
Zwykle ślady stosu są znacznie bardziej złożone niż dwa powyższe przykłady. Oto przykład (jest długi, ale pokazuje kilka poziomów wyjątków łańcuchowych):
W tym przykładzie jest o wiele więcej. Najbardziej martwi nas szukanie metod z naszego kodu , które mogłyby znajdować się w
com.example.myproject
pakiecie. Z drugiego przykładu (powyżej) chcielibyśmy najpierw spojrzeć w dół na główną przyczynę, którą jest:Jednak wszystkie wywołania metod w ramach tego kodu są kodami bibliotek. Przejdziemy więc do powyższego „Caused by” i poszukajmy pierwszego wywołania metody pochodzącego z naszego kodu, którym jest:
Podobnie jak w poprzednich przykładach, powinniśmy spojrzeć na
MyEntityService.java
on-line59
, ponieważ właśnie tam powstał ten błąd (to jest trochę oczywiste, co poszło nie tak, ponieważ SQLException określa błąd, ale o to chodzi w procedurze debugowania).źródło
Exception in thread "main"
w pierwszym przykładzie. Myślę, że szczególnie pomocne byłoby wyjaśnienie, że temu wierszowi często towarzyszy komunikat, taki jak wartość zmiennej, który może pomóc w zdiagnozowaniu problemu. Sam próbowałem dokonać edycji, ale staram się dopasować te pomysły do istniejącej struktury Twojej odpowiedzi.Publikuję tę odpowiedź, więc najwyższa odpowiedź (po posortowaniu według aktywności) nie jest po prostu błędna.
Co to jest Stacktrace?
Stacktrace to bardzo pomocne narzędzie do debugowania. Pokazuje stos wywołań (czyli stos funkcji, które zostały wywołane do tego momentu) w momencie zgłoszenia nieprzechwyconego wyjątku (lub czasu wygenerowania śledzenia stosu ręcznie). Jest to bardzo przydatne, ponieważ pokazuje nie tylko miejsce wystąpienia błędu, ale także sposób, w jaki program znalazł się w tym miejscu kodu. To prowadzi do następnego pytania:
Co to jest wyjątek?
Wyjątkiem jest środowisko uruchomieniowe, które informuje o wystąpieniu błędu. Popularne przykłady to NullPointerException, IndexOutOfBoundsException lub ArithmeticException. Każde z nich powstaje, gdy próbujesz zrobić coś, co nie jest możliwe. Na przykład wyjątek NullPointerException zostanie zgłoszony, gdy spróbujesz wyrejestrować obiekt Null:
Jak mam radzić sobie z Stacktraces / Exceptions?
Najpierw dowiedz się, co powoduje wyjątek. Spróbuj wyszukać w Google nazwę wyjątku, aby dowiedzieć się, co jest przyczyną tego wyjątku. W większości przypadków będzie to spowodowane nieprawidłowym kodem. W podanych powyżej przykładach wszystkie wyjątki są spowodowane niepoprawnym kodem. Tak więc dla przykładu NullPointerException możesz upewnić się, że
a
w tym momencie nigdy nie ma wartości null. Możesz na przykład zainicjowaća
lub dołączyć czek podobny do tego:W ten sposób linia naruszająca nie jest wykonywana, jeśli
a==null
. To samo dotyczy innych przykładów.Czasami nie możesz się upewnić, że nie otrzymasz wyjątku. Na przykład, jeśli korzystasz z połączenia sieciowego w swoim programie, nie możesz powstrzymać komputera przed utratą połączenia internetowego (np. Nie możesz powstrzymać użytkownika przed rozłączeniem połączenia sieciowego komputera). W takim przypadku biblioteka sieciowa prawdopodobnie zgłosi wyjątek. Teraz powinieneś złapać wyjątek i go obsłużyć . Oznacza to, że w przykładzie z połączeniem sieciowym powinieneś spróbować ponownie otworzyć połączenie lub powiadomić użytkownika lub coś w tym rodzaju. Ponadto, ilekroć korzystasz z catch, zawsze wychwytuj tylko wyjątek, który chcesz złapać, nie używaj instrukcji typu catch broad, takich jak
catch (Exception e)
to złapałoby wszystkie wyjątki. Jest to bardzo ważne, ponieważ w przeciwnym razie możesz przypadkowo złapać niewłaściwy wyjątek i zareagować w niewłaściwy sposób.Dlaczego nie powinienem używać
catch (Exception e)
?Użyjmy małego przykładu, aby pokazać, dlaczego nie powinieneś po prostu złapać wszystkich wyjątków:
To, co ten kod próbuje zrobić, to złapanie
ArithmeticException
przyczyny spowodowanej możliwym dzieleniem przez 0. Ale łapie także możliwe,NullPointerException
które zostanie wyrzucone, jeślia
lubb
sąnull
. Oznacza to, że możesz dostać a,NullPointerException
ale potraktujesz to jako wyjątek arytmetyczny i prawdopodobnie zrobisz coś złego. W najlepszym przypadku nadal brakuje Ci wyjątku NullPointerException. Takie rzeczy znacznie utrudniają debugowanie, więc nie rób tego.TLDR
Jeśli 1. nie jest możliwe, złap konkretny wyjątek i obsłuż go.
catch (Exception e)
, zawsze wychwytuj określone wyjątki. Dzięki temu zaoszczędzisz wiele bólów głowy.źródło
Aby dodać do tego, o czym wspomniał Rob. Ustawienie punktów przerwania w aplikacji umożliwia przetwarzanie stosu krok po kroku. Dzięki temu programiści mogą skorzystać z debugera, aby zobaczyć, w którym momencie metoda robi coś nieoczekiwanego.
Ponieważ Rob wykorzystał
NullPointerException
(NPE) do zilustrowania czegoś powszechnego, możemy pomóc w usunięciu tego problemu w następujący sposób:jeśli mamy metodę, która przyjmuje parametry takie jak:
void (String firstName)
W naszym kodzie chcielibyśmy ocenić, czy
firstName
zawiera wartość, zrobilibyśmy to w następujący sposób:if(firstName == null || firstName.equals("")) return;
Powyższe uniemożliwia nam używanie
firstName
jako niebezpiecznego parametru. Dlatego przeprowadzając kontrole zerowe przed przetwarzaniem, możemy pomóc zapewnić prawidłowe działanie naszego kodu. Aby rozwinąć na przykładzie, który wykorzystuje obiekt metodami, możemy zajrzeć tutaj:if(dog == null || dog.firstName == null) return;
Powyżej jest właściwa kolejność sprawdzania wartości zerowych, zaczynamy od obiektu podstawowego, w tym przypadku psa, a następnie zaczynamy chodzić po drzewie możliwości, aby upewnić się, że wszystko jest poprawne przed przetwarzaniem. Jeśli zamówienie zostanie odwrócone, potencjalnie NPE może zostać wyrzucony, a nasz program ulegnie awarii.
źródło
null
sięNullPointerException
na przykład podczas sprawdzania.Jest jeszcze jedna funkcja śledzenia stosu oferowana przez rodzinę Throwable - możliwość manipulowania informacjami śledzenia stosu.
Standardowe zachowanie:
Ślad stosu:
Manipulowany ślad stosu:
Ślad stosu:
źródło
Aby zrozumieć nazwę : Śledzenie stosu to lista wyjątków (lub można powiedzieć listę „Przyczyna przez”), od najbardziej wyjątkowego na powierzchni (np. Wyjątek warstwy usług) do najgłębszego (np. Wyjątek bazy danych). Tak jak nazywamy to „stosem”, ponieważ stos jest pierwszym na ostatnim wyjściu (FILO), najgłębszy wyjątek miał miejsce na samym początku, a następnie wygenerowano łańcuch wyjątków z szeregiem konsekwencji, wyjątek powierzchniowy był ostatnim jedno wydarzyło się w czasie, ale przede wszystkim to widzimy.
Klucz 1 : Trudną i ważną rzeczą, którą należy tutaj zrozumieć, jest: najgłębsza przyczyna może nie być „pierwotną przyczyną”, ponieważ jeśli napiszesz jakiś „zły kod”, może to spowodować wyjątek, który jest głębszy niż jego warstwa. Na przykład niepoprawne zapytanie SQL może spowodować reset połączenia SQLServerException na dole zamiast błędu syndax, który może znajdować się na środku stosu.
-> Znajdź główną przyczynę pośrodku, to twoja praca.
Klucz 2 : Kolejną trudną, ale ważną rzeczą jest każdy blok „Przyczyna”, pierwsza linia była najgłębszą warstwą i zajmowała pierwsze miejsce dla tego bloku. Na przykład,
Book.java:16 został wywołany przez Auther.java:25, który został wywołany przez Bootstrap.java:14, Book.java:16 był główną przyczyną. Tutaj dołącz diagram posortuj stos śledzenia w kolejności chronologicznej.
źródło
Aby dodać do innych przykładów, istnieją wewnętrzne (zagnieżdżone) klasy, które pojawiają się wraz ze
$
znakiem. Na przykład:Spowoduje to śledzenie stosu:
źródło
Pozostałe posty opisują, co to jest ślad stosu, ale nadal może być ciężko pracować.
Jeśli otrzymujesz śledzenie stosu i chcesz prześledzić przyczynę wyjątku, dobrym punktem wyjścia do zrozumienia jest użycie konsoli śledzenia stosu Java w Eclipse . Jeśli użyjesz innego IDE, może istnieć podobna funkcja, ale ta odpowiedź dotyczy Eclipse.
Po pierwsze, upewnij się, że masz wszystkie źródła Java dostępne w projekcie Eclipse.
Następnie w perspektywie Java kliknij kartę Konsola (zwykle u dołu). Jeśli widok konsoli nie jest widoczny, przejdź do opcji menu Okno -> Pokaż widok i wybierz Konsolę .
Następnie w oknie konsoli kliknij następujący przycisk (po prawej)
a następnie wybierz Konsolę śledzenia stosu Java z listy rozwijanej.
Wklej ślad stosu do konsoli. Następnie udostępni listę linków do kodu źródłowego i dowolnego innego dostępnego kodu źródłowego.
Oto, co możesz zobaczyć (obraz z dokumentacji Eclipse):
Najnowszym wywołaniem metody będzie górna część stosu, która jest górną linią (bez tekstu wiadomości). Schodzenie w dół stosu cofa się w czasie. Druga linia to metoda wywołująca pierwszą linię itp.
Jeśli korzystasz z oprogramowania typu open source, może być konieczne pobranie i dołączenie do projektu źródeł, jeśli chcesz je zbadać. Pobierz słoiki źródłowe, w swoim projekcie otwórz folder Biblioteki referencyjne, aby znaleźć słoik dla modułu open source (ten z plikami klas), a następnie kliknij prawym przyciskiem myszy, wybierz Właściwości i dołącz słoik źródłowy.
źródło