1. Uzyskanie śladu stosu

Pobieranie śladu stosu

W języku programowania Java programista ma wiele sposobów na uzyskanie informacji o tym, co aktualnie dzieje się w programie. I to nie tylko słowa.

Na przykład programy C++ po kompilacji zamieniają się w jeden duży plik kodu maszynowego, a wszystko, co jest dostępne dla programisty podczas wykonywania, to adres fragmentu pamięci, który zawiera aktualnie wykonywany kod maszynowy. Nie dużo, powiedzmy.

W Javie nawet po kompilacji klasy pozostają klasami, metody i zmienne nie znikają, a programista ma wiele sposobów na uzyskanie informacji o tym, co aktualnie dzieje się w programie.

Ślad stosu

Na przykład w dowolnym momencie działania programu można znaleźć klasę i nazwę aktualnie wykonywanej metody. I nawet nie jedną metodę, ale uzyskaj informacje o całym łańcuchu wywołań metod od bieżącej metody do main().

Lista składająca się z bieżącej metody, metody, która ją wywołała, metody, która ją wywołała itd., nazywana jest śladem stosu . Możesz go uzyskać za pomocą polecenia:

StackTraceElement[] methods = Thread.currentThread().getStackTrace();

Możesz też napisać to w dwóch liniach:

Thread current = Thread.currentThread();
StackTraceElement[] methods = current.getStackTrace();

Metoda currentThread()klasy statycznej Threadzwraca odwołanie do obiektu typu Thread, który zawiera informacje o bieżącym wątku (o bieżącym wątku wykonania). Więcej o wątkach dowiesz się w zadaniu Java Core .

Ten obiekt Threadma metodę getStackTrace(), która zwraca tablicę elementów StackTraceElement, z których każdy zawiera informacje o jednej metodzie. Wszystkie elementy razem tworzą ślad stosu .

Przykład:

Kod
public class Main
{
   public static void main(String[] args)
   {
      test();
   }

   public static void test()
   {
      Thread current = Thread.currentThread();
      StackTraceElement[] methods = current.getStackTrace();

      for(var info: methods)
         System.out.println(info);
   }
}
Wyjście na wyświetlaczu
java.base/java.lang.Thread.getStackTrace(Thread.java:1606)
Main.test(Main.java:11)
Main.main(Main.java:5)

Jak widać z danych wyjściowych na ekranie, w powyższym przykładzie metoda getStackTrace()zwróciła tablicę złożoną z trzech elementów:

  • metoda getStackTrace()klasowaThread
  • metoda test()klasowaMain
  • metoda main()klasowaMain

Z tego śladu stosu możemy wywnioskować, że:

  • Metoda Thread.getStackTrace()została wywołana przez metodę Main.test()w linii 11 pliku Main.java
  • Metoda Main.test()została wywołana metodą Main.main()w wierszu 5 pliku Main.java
  • Main.main()Nikt nie wywołał metody - jest to pierwsza metoda w łańcuchu wywołań.

Nawiasem mówiąc, na ekranie wyświetlała się tylko część wszystkich dostępnych informacji. Wszystko inne można uzyskać bezpośrednio z obiektuStackTraceElement



2.StackTraceElement

Klasa StackTraceElement, jak sama nazwa wskazuje, jest przeznaczona do przechowywania informacji o pojedynczym elemencie śladu stosu – tj. jedna metoda z StackTrace.

Obiekty tej klasy mają następujące metody:

metoda Opis
String getClassName()
Zwraca nazwę klasy
String getMethodName()
Zwraca nazwę metody
String getFileName()
Zwraca nazwę pliku (jeden plik może mieć wiele klas)
int getLineNumber()
Zwraca numer wiersza w pliku, w którym wywołano metodę
String getModuleName()
Zwraca nazwę modułu (może być null)
String getModuleVersion()
Zwraca wersję modułu (może być null)

Z ich pomocą możesz uzyskać pełniejsze informacje o bieżącym stosie wywołań:

Kod Wyjście na wyświetlaczu Notatka
public class Main
{
   public static void main(String[] args)
   {
      test();
   }

   public static void test()
   {
      Thread current = Thread.currentThread();
      StackTraceElement[] methods = current.getStackTrace();

      for(StackTraceElement info: methods)
      {
         System.out.println(info.getClassName());
         System.out.println(info.getMethodName());

         System.out.println(info.getFileName());
         System.out.println(info.getLineNumber());

         System.out.println(info.getModuleName());
         System.out.println(info.getModuleVersion());
         System.out.println();
      }
   }
}
java.lang.Thread
getStackTrace
Thread.java
1606
java.base
11.0.2

Main
test
Main.java
11
null
null

Main
main
Main.java
5
null
null
nazwa klasy
nazwa metody
nazwa pliku
numer linii
nazwa modułu wersja modułu nazwa klasy nazwa metody nazwa pliku numer linii
nazwa modułu wersja modułu nazwa klasy nazwa metody nazwa pliku nazwa linii numer linii nazwa modułu wersja modułu















3. Stos

Co to jest Stack Trace, które już znasz, ale czym jest sam Stack ?

Stos to struktura przechowywania danych, do której można dodawać elementy iz której elementy można pobierać. Co więcej, możesz pobierać elementy tylko od końca: najpierw ostatni dodany, potem przedostatni i tak dalej.

Sama nazwa Stos jest tłumaczona z angielskiego jako „stos” i jest bardzo podobna do stosu papieru. Jeśli ułożysz arkusze 1, 2 i 3 na stosie papieru, możesz je wziąć tylko w odwrotnej kolejności: najpierw trzeci, potem drugi, a dopiero potem pierwszy.

Java ma nawet specjalną kolekcję o takim samym zachowaniu i tej samej nazwie — Stack. Ta klasa jest bardzo podobna w zachowaniu do ArrayListi LinkedList. Ma jednak więcej metod, które implementują zachowanie stosu:

Metody Opis
T push(T obj)
Dodaje element objna koniec listy (na górę stosu)
T pop()
Podnosi element ze szczytu stosu (wysokość stosu maleje)
T peek()
Zwraca element ze szczytu stosu (stos się nie zmienia)
boolean empty()
Sprawdza, czy kolekcja jest pusta
int search(Object obj)
Wyszukuje obiekt z kolekcji, zwraca goindex

Przykład:

Kod Zawartość stosu (prawy górny róg)
Stack<Integer> stack = new Stack<Integer>():
stack.push(1);
stack.push(2);
stack.push(3);
int x = stack.pop();
stack.push(4);
int y = stack.peek();
stack.pop();
stack.pop();

[1]
[1, 2]
[1, 2, 3]
[1, 2]
[1, 2, 4]
[1, 2, 4]
[1, 2]
[1]

Stos jest dość często używany w programowaniu. Jest to więc przydatna kolekcja.



4. Układaj dane wyjściowe śledzenia podczas obsługi błędów

Dlaczego lista wywołań metod nosi nazwę StackTrace ? Tak, ponieważ jeśli wyobrazisz sobie listę metod jako stos arkuszy z nazwami metod, gdy wywołasz następną metodę, arkusz z nazwą metody zostanie umieszczony na tym stosie, na nim zostanie umieszczony następny i tak NA.

Po zakończeniu metody liść z wierzchu stosu jest usuwany. Nie możesz usunąć liścia ze środka stosu bez usunięcia wszystkich leżących na nim arkuszy - nie możesz zatrzymać działania metody w łańcuchu wywołań bez ukończenia wszystkich wywoływanych przez nią metod.

Wyjątki

Innym interesującym zastosowaniem stosu jest obsługa wyjątków.

Gdy w programie wystąpi błąd i zostanie zgłoszony wyjątek , zapisywany jest do niego bieżący ślad stosu : tablica składająca się z listy metod, zaczynając od metody głównej i kończąc na metodzie, w której wystąpił błąd. Jest nawet wiersz, w którym został zgłoszony wyjątek!

Ten ślad stosu błędu jest przechowywany wewnątrz wyjątku i można go łatwo odzyskać za pomocą metody:StackTraceElement[] getStackTrace()

Przykład:

Kod Notatka
try
{
   // тут может возникнуть исключение
}
catch(Exception e)
{
   StackTraceElement[] methods = e.getStackTrace()
}




Przechwyć wyjątek

Uzyskaj z niego ślad stosu w momencie wystąpienia błędu.

Jest to metoda klasowa Throwable, co oznacza, że ​​wszystkie jej klasy potomne (czyli ogólnie wszystkie wyjątki) mają metodę getStackTrace(). Bardzo wygodne, prawda?

Drukowanie śladu stosu błędów

Nawiasem mówiąc, klasa Throwablema jeszcze jedną metodę pracy ze śledzeniem stosu: wypisuje na konsoli wszystkie informacje dotyczące śledzenia stosu, które są przechowywane w wyjątku. Tak to się nazywa printStackTrace().

Możesz wywołać go na dowolnym wyjątku, co jest bardzo wygodne.

Przykład:

Kod
try
{
   // тут может возникнуть исключение
}
catch(Exception e)
{
   e.printStackTrace();
}
Wyjście na wyświetlaczu
java.base/java.lang.Thread.getStackTrace(Thread.java:1606)
Main.test(Main.java:11)
Main.main(Main.java:5)