1. Verem nyomkövetése

A verem nyomának lekérése

A Java programozási nyelv számos lehetőséget kínál a programozó számára, hogy információt szerezzen arról, hogy mi történik a programban. És nem csak szavak.

Például a C++ programok lefordítása után egyetlen nagy fájl lesz, amely tele van gépi kóddal, és futás közben csak az éppen végrehajtott gépi kódot tartalmazó memóriablokk címe áll a programozó rendelkezésére. Mondjuk nem sok.

A Java esetében azonban az osztályok a program lefordítása után is osztályok maradnak, a metódusok és a változók nem tűnnek el, és a programozónak számos módja van, hogy információt szerezzen a programban zajló eseményekről.

Veremnyom

Például egy program végrehajtásának pontján megtudhatja az éppen végrehajtott metódus osztályát és nevét. És nem csak egy metódus – információkat kaphat a metódushívások teljes láncáról az aktuális metódustól a metódusig main().

Az aktuális metódusból és az azt meghívó metódusból és az azt meghívó metódusból stb. álló listát veremkövetésnek nevezzük . Ezzel a nyilatkozattal érheti el:

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

Két sorban is írhatod:

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

Az osztály statikus currentThread()metódusa Threadegy hivatkozást ad vissza egy objektumra Thread, amely információt tartalmaz az aktuális szálról, azaz az aktuális végrehajtási szálról. A Java Core küldetés 17. és 18. szintjén lévő szálakról többet megtudhat .

Ennek Threadaz objektumnak van egy getStackTrace()metódusa, amely objektumok tömbjét adja vissza StackTraceElement, amelyek mindegyike egy metódusra vonatkozó információkat tartalmaz. Mindezek az elemek együttesen egy veremnyomot alkotnak .

Példa:

Kód
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);
   }
}
Konzol kimenet
java.base/java.lang.Thread.getStackTrace(Thread.java:1606)
Main.test(Main.java:11)
Main.main(Main.java:5)

Amint a példa konzol kimenetén láthatjuk, a getStackTrace()metódus három elemből álló tömböt adott vissza:

  • getStackTrace()Threadosztály módszere
  • test()Mainosztály módszere
  • main()Mainosztály módszere

Ebből a veremnyomból a következőkre következtethetünk:

  • A metódust a Main.java fájl 11. sorában található metódus Thread.getStackTrace()hívta megMain.test()
  • A metódust a Main.java fájl 5. sorában található metódus Main.test()hívta megMain.main()
  • Senki nem hívta a Main.main()metódust – ez az első módszer a hívások láncolatában.

A képernyőn egyébként csak néhány elérhető információ jelent meg. StackTraceElementMinden mást közvetlenül az objektumtól kaphatunk



2.StackTraceElement

Ahogy a neve is sugallja, az StackTraceElementosztályt úgy hozták létre, hogy információkat tároljon egy verem nyomelemről , azaz az egyik metódusról a stack trace.

Ennek az osztálynak a következő példánymódszerei vannak:

Módszer Leírás
String getClassName()
Az osztály nevét adja vissza
String getMethodName()
A metódus nevét adja vissza
String getFileName()
A fájl nevét adja vissza (egy fájl több osztályt is tartalmazhat)
int getLineNumber()
A fájl sorszámát adja vissza, ahol a metódus meghívásra került
String getModuleName()
A modul nevét adja vissza (ez lehet null)
String getModuleVersion()
A modul verzióját adja vissza (ez lehet null)

Segítségükkel teljesebb információkat kaphat az aktuális hívásveremről:

Kód Konzol kimenet jegyzet
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
osztálynév
metódusnév
fájlnév
sorszám
modulnév
modul verzió

osztálynév
metódusnév
fájlnév
sorszám
modulnév modul
verzió

osztálynév
metódusnév
fájlnév
sorszám
modulnév
modul verzió


3. Verem

Már tudod, mi az a veremnyom , de mi az a verem (Stack class)?

A verem egy olyan adatstruktúra, amelyhez elemeket adhat hozzá, és amelyből elemeket kérhet le. Ennek során csak a végéről vehetünk át elemeket: először az utoljára hozzáadott, majd a második utolsót stb.

Maga a köteg név is ezt a viselkedést sugallja, például azt, hogy hogyan kommunikálna egy köteg papírral. Ha az 1., 2. és 3. lapot egy kötegbe rakja, akkor fordított sorrendben kell elővennie őket: először a harmadik lapot, majd a másodikat, és csak azután az elsőt.

A Java-nak még egy speciális Stack gyűjteményosztálya is van, ugyanazzal a névvel és viselkedéssel. Ez az osztály sok magatartást tanúsít ArrayListés LinkedList. De vannak olyan módszerei is, amelyek a verem viselkedését valósítják meg:

Mód Leírás
T push(T obj)
Hozzáadja az objelemet a verem tetejéhez
T pop()
Elveszi az elemet a verem tetejéről (a verem mélysége csökken)
T peek()
A köteg tetején lévő elemet adja vissza (a köteg nem változik)
boolean empty()
Ellenőrzi, hogy a gyűjtemény üres-e
int search(Object obj)
Megkeres egy objektumot a gyűjteményben, és visszaadja aztindex

Példa:

Kód A halom tartalma (a köteg teteje a jobb oldalon található)
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]

A veremeket meglehetősen gyakran használják a programozásban. Szóval ez egy hasznos gyűjtemény.



4. Veremkövetés megjelenítése kivételkezelés során

Miért nevezik a metódushívások listáját veremkövetésnek ? Mert ha a módszerek listáját metódusnevekkel ellátott papírlapok kötegének tekinti, akkor a következő metódus meghívásakor hozzáad egy lapot a módszer nevével a köteghez. És a következő papírlap erre megy, és így tovább.

Amikor egy módszer véget ér, a köteg tetején lévő lapot eltávolítjuk. Nem távolíthat el egy lapot a köteg közepéről anélkül, hogy eltávolítaná az összes felette lévő lapot. Hasonlóképpen, nem zárhat le egy metódust a hívások láncolatának közepén anélkül, hogy az összes meghívott metódust ne befejezné.

Kivételek

A veremek másik érdekes felhasználási módja a kivételkezelés.

Ha hiba történik egy programban, és kivételt adnak ki , a kivétel tartalmazza az aktuális verem nyomkövetéstegy tömböt, amely metódusok listájából áll, a fő metódustól kezdve a hiba előfordulásának módszerével végződve. Még az a sor is ott van, ahol a kivételt dobták!

Ez a veremnyom a kivételen belül van tárolva, és könnyen visszakereshető belőle a következő módszerrel:StackTraceElement[] getStackTrace()

Példa:

Kód jegyzet
try
{
   // An exception may occur here
}
catch(Exception e)
{
   StackTraceElement[] methods = e.getStackTrace()
}




A kivétel elkapása

Szerezze le a verem nyomkövetését, amely a hiba bekövetkeztekor létezett.

Ez az osztály metódusa Throwable, tehát minden leszármazottja (vagyis minden kivétel) rendelkezik ezzel a getStackTrace()módszerrel. Szuper kényelmes, mi?

A kivétel veremnyomának megjelenítése

Egyébként az Throwableosztálynak van egy másik metódusa a verem nyomkövetésekkel való munkához, egy olyan metódus, amely megjeleníti a kivételen belül tárolt összes veremnyomkövetési információt. Úgy hívják printStackTrace().

Meglehetősen kényelmesen, bármilyen kivétellel hívhatja.

Példa:

Kód
try
{
   // An exception may occur here
}
catch(Exception e)
{
   e.printStackTrace();
}
Konzol kimenet
java.base/java.lang.Thread.getStackTrace(Thread.java:1606)
Main.test(Main.java:11)
Main.main(Main.java:5)