1. Obținerea unei urme a stivei

Obținerea unei urme a stivei

Limbajul de programare Java oferă un programator mai multe modalități de a obține informații despre ceea ce se întâmplă într-un program. Și nu doar cuvinte.

De exemplu, după ce programele C++ sunt compilate, acestea devin un fișier mare plin de cod de mașină și tot ceea ce este disponibil pentru un programator în timpul rulării este adresa blocului de memorie care conține codul de mașină în curs de executare. Nu multe, să zicem.

Dar pentru Java, chiar și după ce un program este compilat, clasele rămân clase, metodele și variabilele nu dispar, iar programatorul are multe modalități de a obține informații despre ceea ce se întâmplă în program.

Urmă stivă

De exemplu, în momentul execuției unui program, puteți afla clasa și numele metodei în curs de executare. Și nu doar o metodă - puteți obține informații despre întregul lanț de apeluri de metodă de la metoda curentă înapoi la metodă main().

O listă care constă din metoda curentă și metoda care a invocat-o și metoda care a numit-o pe aceea etc. se numește urmă de stivă . O poți obține cu această declarație:

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

De asemenea, îl puteți scrie pe două rânduri:

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

Metoda statică currentThread()a Threadclasei returnează o referință la un Threadobiect, care conține informații despre firul curent, adică firul curent de execuție. Veți afla mai multe despre firele de discuție la nivelurile 17 și 18 ale misiunii Java Core .

Acest Threadobiect are o getStackTrace()metodă, care returnează o matrice de StackTraceElementobiecte, fiecare dintre ele conținând informații despre o metodă. Luate împreună, toate aceste elemente formează o urmă de stivă .

Exemplu:

Cod
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);
   }
}
Ieșire de consolă
java.base/java.lang.Thread.getStackTrace(Thread.java:1606)
Main.test(Main.java:11)
Main.main(Main.java:5)

După cum putem vedea în rezultatul consolei din exemplu, getStackTrace()metoda a returnat o matrice de trei elemente:

  • getStackTrace()metoda Threadclasei
  • test()metoda Mainclasei
  • main()metoda Mainclasei

Din această urmărire a stivei, putem concluziona că:

  • Metoda Thread.getStackTrace()a fost apelată prin Main.test()metoda de pe linia 11 a fișierului Main.java
  • Metoda Main.test()a fost apelată prin Main.main()metoda de pe linia 5 a fișierului Main.java
  • Nimeni nu a numit Main.main()metoda — aceasta este prima metodă din lanțul de apeluri.

Apropo, doar o parte din informațiile disponibile au fost afișate pe ecran. Orice altceva poate fi obținut direct de la StackTraceElementobiect



2.StackTraceElement

După cum sugerează și numele, StackTraceElementclasa a fost creată pentru a stoca informații despre un element de urmărire a stivei , adică o metodă din stack trace.

Această clasă are următoarele metode de instanță:

Metodă Descriere
String getClassName()
Returnează numele clasei
String getMethodName()
Returnează numele metodei
String getFileName()
Returnează numele fișierului (un fișier poate conține mai multe clase)
int getLineNumber()
Returnează numărul liniei din fișierul în care a fost apelată metoda
String getModuleName()
Returnează numele modulului (acesta poate fi null)
String getModuleVersion()
Returnează versiunea modulului (aceasta poate fi null)

Acestea vă pot ajuta să obțineți informații mai complete despre stiva de apeluri curentă:

Cod Ieșire de consolă Notă
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
Nume de clasă Nume
de metodă
Nume de fișier
Număr de linie
Nume de
modul Versiune

de modul
Nume de clasă Nume de metodă
Nume de fișier Numă de linie Nume de modul
Modul de versiune Nume de clasă Nume de metodă Nume de fișier Nume de linie Nume de linie Nume de modul Modul de versiune










3. Stiva

Știți deja ce este o urmă de stivă , dar ce este o stivă (clasa stivă)?

O stivă este o structură de date la care puteți adăuga elemente și din care puteți prelua elemente. Procedând astfel, puteți lua doar elemente de la sfârșit: mai întâi îl luați pe ultimul adăugat, apoi pe ultimul adăugat, etc.

Teancul de nume în sine sugerează acest comportament, cum ar fi modul în care ați interacționa cu un teanc de hârtie. Dacă puneți foile 1, 2 și 3 într-un teanc, trebuie să le recuperați în ordine inversă: mai întâi a treia foaie, apoi a doua și abia apoi prima.

Java are chiar și o clasă specială de colecție Stack cu același nume și același comportament. Această clasă împărtășește o mulțime de comportamente cu ArrayListși LinkedList. Dar are și metode care implementează comportamentul stivei:

Metode Descriere
T push(T obj)
Adaugă objelementul în partea de sus a stivei
T pop()
Preia elementul din partea de sus a stivei (adâncimea stivei scade)
T peek()
Returnează articolul din partea de sus a stivei (stiva nu se schimbă)
boolean empty()
Verifică dacă colecția este goală
int search(Object obj)
Caută un obiect din colecție și returnează acestaindex

Exemplu:

Cod Conținutul stivei (partea de sus a stivei este în dreapta)
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]

Stivele sunt folosite destul de des în programare. Deci aceasta este o colecție utilă.



4. Afișarea unei urme de stivă în timpul gestionării excepțiilor

De ce o listă de apeluri de metodă este numită urmărire stivă ? Pentru că dacă te gândești la lista de metode ca la un teanc de foi de hârtie cu nume de metode, atunci când apelezi următoarea metodă, adaugi o foaie cu numele acelei metode în teanc. Și următoarea foaie de hârtie merge deasupra acesteia și așa mai departe.

Când o metodă se termină, foaia din partea de sus a stivei este îndepărtată. Nu puteți scoate o foaie din mijlocul stivei fără a îndepărta toate foile de deasupra acesteia. În mod similar, nu puteți termina o metodă în mijlocul unui lanț de apeluri fără a termina toate metodele pe care le-a apelat.

Excepții

O altă utilizare interesantă pentru stive este în timpul gestionării excepțiilor.

Atunci când apare o eroare într-un program și se aruncă o excepție , excepția conține următorul stivă curent - o matrice constând dintr-o listă de metode care pornesc, de la metoda principală și se termină cu metoda în care a apărut eroarea. Există chiar și linia în care a fost aruncată excepția!

Această urmărire a stivei este stocată în interiorul excepției și poate fi extrasă cu ușurință din ea folosind următoarea metodă:StackTraceElement[] getStackTrace()

Exemplu:

Cod Notă
try
{
   // An exception may occur here
}
catch(Exception e)
{
   StackTraceElement[] methods = e.getStackTrace()
}




Prindeți excepția

Obțineți urmărirea stivei care exista atunci când a apărut eroarea.

Aceasta este o metodă a Throwableclasei, deci toți descendenții ei (adică toate excepțiile) au getStackTrace()metoda. Super convenabil, nu?

Afișează urmărirea stivei excepției

Apropo, Throwableclasa are o altă metodă de lucru cu urmele stivei, o metodă care afișează toate informațiile despre urmele stivei stocate în interiorul excepției. Se numește printStackTrace().

Destul de convenabil, îl puteți apela cu orice excepție.

Exemplu:

Cod
try
{
   // An exception may occur here
}
catch(Exception e)
{
   e.printStackTrace();
}
Ieșire de consolă
java.base/java.lang.Thread.getStackTrace(Thread.java:1606)
Main.test(Main.java:11)
Main.main(Main.java:5)