W tym artykule dowiesz się i zrozumiesz, jak działa zjawisko Java StackTrace, znane również jako Call Stack Tracing. Informacje te zostały opracowane z myślą o początkujących, którzy zetknęli się z tą koncepcją na początku 9. poziomu składni języka Java. Myślę, że każdy z Was przynajmniej raz spotkał się z podobnymi błędami podczas pracy w swoim IDE, niezależnie od tego, czy był to Idea , Eclipse , czy coś innego.
Exception in thread "main" java.lang.ArithmeticException
at com.example.task01.Test.division(Test.java:10)
at com.example.task01.Test.main(Test.java:6)
Jak można się domyślić, jest to nasz trop. Ale nie spiesz się z paniką, teraz omówimy dla Ciebie ten przykład. Najpierw musisz zrozumieć fakt, że StackTrace
działa to tak, jak Стэк
sugeruje jego nazwa. W tym momencie zajmiemy się nieco bardziej szczegółowo. Na ósmym poziomie zapoznałeś się już ze zbiorami i wiesz, że są one podzielone na trzy grupy Set
- zbiór, List
- lista, Map
- słownik (lub mapa). Według JavaRush (c). Nasz Stack
jest częścią tej grupy List
. Zasadę jego działania można określić jako LIFO , co oznacza Last In First Out. Mianowicie jest to lista przypominająca stos książek; aby wziąć element, który umieściliśmy jako Stack
pierwszy, musimy najpierw wyodrębnić wszystkie elementy, które dodaliśmy do naszej listy później. Jak widać na powyższym obrazku, w odróżnieniu od np. zwykłej listy, ArrayList
gdzie dowolny element z listy możemy pobrać po indeksie. Jeszcze raz dla wzmocnienia. Pobranie elementu z Стэка
jest możliwe tylko od końca! Natomiast pierwszy dodany do niego element znajduje się na początku (lub na dole, jak wygodniej). Oto metody, jakie ma nasz Stack
Obiekt push()
- Dodaje element na górę stosu. Obiekt pop()
— zwraca element znajdujący się na szczycie stosu, usuwając go przy tym. Obiekt peek()
— zwraca element znajdujący się na szczycie stosu, ale go nie usuwa. int search()
– Wyszukuje element na stosie. Jeśli zostanie znaleziony, zwracane jest jego przesunięcie od góry stosu. W przeciwnym razie zwracane jest -1. boolean empty()
— Sprawdza, czy stos jest pusty. Zwraca wartość true, jeśli stos jest pusty. Zwraca wartość false, jeśli stos zawiera elementy. Po co więc taki, który Java
jest StackTrace
zbudowany na zasadach działania Stack
? Spójrzmy na poniższy przykład błędu, który wystąpił podczas wykonywania tak prostego programu.
public class Test {
public static void main(String[] args) {
System.out.println(convertStringToInt(null));
}
public static int convertStringToInt(String s) {
int x = Integer.parseInt(s);
return x;
}
}
Mamy klasę Test
z dwiema metodami. Każdy jest znany main
i convertStringToInt
jego logika polega na konwersji i zwróceniu ciągu znaków otrzymanego z zewnątrz (czyli z metody main
) na liczbę całkowitą typu int
. Jak widać celowo przekazaliśmy parametr zamiast ciągu znaków z jakąś liczbą null
. Nasza metoda nie mogła poprawnie przetworzyć tego parametru i spowodowała błąd NumberFormatException
. Jak wiadomo program zaczyna wykonywać swoją pracę od metody main
i w tym momencie tworzy nową Стэк
o nazwie gdzie pod cyfrą 1StackTrace
wstawia aktualną wartość swojej pracy , następnie przechodzimy do metody i programu jeszcze raz wpisze parametry naszej lokalizacji do utworzonej wcześniej pod numerem 2 , wówczas nazywa się to metodą niewidzialną dla naszych oczu znajdującą się w klasie i będzie to już nasz element numer 3 , w tej metodzie zostanie dodane kolejne wywołanie wewnętrzne do numeru 4 , aby sprawdzić, czy element ma wartość null , co doprowadzi do błędu. Program musi wyświetlić nasz błąd wskazując cały łańcuch naszych przejść aż do wystąpienia błędu. Tu z pomocą przychodzi jej ta wcześniej utworzona z danymi naszych przejść . convertStringToInt
StackTrace
parseInt
Integer
StackTrace
StackTrace
StackTrace
Exception in thread "main" java.lang.NumberFormatException: null
at java.base/java.lang.Integer.parseInt(Integer.java:614)
at java.base/java.lang.Integer.parseInt(Integer.java:770)
at com.example.task01.Test.convertStringToInt(Solution.java:10)
at com.example.task01.Test.main(Solution.java:6)
Zanim pojawił się błąd, program zagłębił się w metody, ale gdy tylko pojawił się błąd, wszystko zaczyna się dziać w odwrotnej kolejności. Wydrukowana zostanie linia opisująca problem (w przykładzie nr 1), następnie zostanie pobrana ostatnia (i na górze) wartość dodana do naszej, Стэк
była to liczba czwarta i wypisana do konsoli (w przykładzie nr 2) i widzimy, że problem pojawił się w klasie Integer
w linii kodu 614 i nazwał tę linię linią 770 metody parseInt
tej samej klasy (nr 3 w przykładzie), która po dodaniu Стэк
była numerem trzy i tą metodą klasy, Integer
nadal dla nas niewidoczna, została już wywołana przez naszą metodę convertStringToInt
znajdującą się w linii 10 naszego programu (nr 4 w przykładzie, a przy dodawaniu była druga), a ta z kolei została wywołana main
w linii 6 (nr 5 w przykładzie i odpowiednio podczas dodawania pierwszego). Tak więc, przechowując Стек
krok po kroku wywoływane przez nas metody, mogliśmy powrócić do main
równoległego drukowania informacji, które dokładnie doprowadziły nas do błędu. Ale StackTrace
to nie tylko praca z błędami, pozwala nam uzyskać wiele ciekawych informacji na temat procesu naszej aplikacji. Spójrzmy na inny popularny przykład w komentarzach do wykładu głównego poziomu 9. Mamy kod i od razu dołączę do niego obrazek wizualizujący przebieg programu:
public class Test {
public static void main(String[] args) {
method1();
method2();
}
public static void method1() {
//не вызывает ничего
}
public static void method2() {
method3();
method4();
}
public static void method3() {
//не вызывает ничего
}
public static void method4() {
method5();
}
public static void method5() {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (StackTraceElement element:stackTraceElements) {
System.out.println(element.getMethodName());
}
}
}
Tutaj nasz program wykonuje swoje zadanie bezbłędnie i kończy się. Oto, co zobaczymy w wynikach konsoli:
getStackTrace
method5
method4
method2
main
Process finished with exit code 0
Jak doszliśmy do tego wniosku i co się stało w piątej metodzie, zaczynając od wiersza 20? Obawiam się, że najlepsze, co mogę zrobić, to dodać najpopularniejsze wyjaśnienie (w skrócie) użytkownika Kirilla z komentarzy do wykładu. Przejdźmy do linii tworzenia StackTrace
i przeanalizujmy ją element po elemencie:
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement[]
- wskazanie typu tablicy (na wczesnych poziomach poznałeś już tablice takie jak int[], String[], tutaj jest tak samo). stackTraceElements
- nazwa tablicy może być dowolna, biorąc pod uwagę ogólne zasady nazewnictwa, nie ma to wpływu na pracę. Thread.currentThread()
- uzyskanie linku do bieżącego wątku, w którym wykonywane są metody, które chcemy śledzić (na razie to nie jest istotne, wątki przeanalizujesz dokładniej na poziomie 16 w zadaniu Java Core) getStackTrace()
- otrzymujemy wszystkie Стэк
wywołane metody (To jest zwykły getter dla StackTrace
) Zobaczmy teraz, co Utworzona tablica może nam się przydać. Rozumiemy, że tablica przechowuje informacje o wykonanych metodach. (c) I w tym celu w 21. linijce uruchamiamy zmodyfikowany cykl for
o nazwie forEach
(swoją drogą, tym, którzy jeszcze tego cyklu nie przestudiowali, radzę o tym poczytać) i wyprowadzamy dane z tablicy do konsoli , czyli informację o tym , jakimi metodami zastosowano się podczas prac z wykorzystaniem konstrukcji element.getMethodName()
. Uwaga, jak widzimy, elementem zerowym tablicy okazał się getStackTrace()
odpowiednio on sam, ponieważ w momencie otrzymania tablicy danych była to ostatnia metoda, która została wykonana i tym samym znalazła się na górze Стэка
i pamiętając o naszej konstrukcji „ Last in, First out ” jest natychmiast pierwszym, który zostanie dodany do tablicy pod elementem zerowym. Oto, co jeszcze możemy uzyskać z StackTraceElement
: String getClassName()
- Zwraca nazwę klasy. String getMethodName()
— Zwraca nazwę metody. String getFileName()
- Zwraca nazwę pliku (w jednym pliku może znajdować się wiele klas). String getModuleName()
- Zwraca nazwę modułu (może mieć wartość null). String getModuleVersion()
- Zwraca wersję modułu (może mieć wartość null). int getLineNumber()
- Zwraca numer linii w pliku, w którym wywołano metodę. Teraz, gdy rozumiesz już ogólną zasadę działania, radzę ci samemu wypróbować różne metody StackTrace
w swoim Ide . Nawet jeśli nie opanowałeś wszystkiego do końca, kontynuuj naukę, a zagadka ułoży się tak samo, jak u mnie w tej kwestii. Życzę wszystkim sukcesów! Ps Jeśli spodobał Ci się ten materiał, wesprzyj go lajkiem. Nie jest to dla ciebie trudne, miło mi. Dziękuję i do zobaczenia na poziomie 41 ;)
GO TO FULL VERSION