1. Получаване на проследяване на стека

Получаване на проследяване на стека

Езикът за програмиране Java предлага много начини за програмиста да получи информация за това, което се случва в програмата. И не само на думи.

Например, след като C++ програмите са компorрани, те се превръщат в един голям файл, пълен с машинен code, и всичко, което е достъпно за програмиста по време на изпълнение, е addressът на блока памет, който съдържа машинния code, който се изпълнява в момента. Не много, да кажем.

Но за Java, дори след като програмата е компorрана, класовете си остават класове, методите и променливите не изчезват и програмистът има много начини да получи информация за това, което се случва в програмата.

Проследяване на стека

Например, в момент от изпълнението на програма, можете да разберете класа и името на метода, който се изпълнява в момента. И не само един метод — можете да получите информация за цялата верига от извиквания на метод от текущия метод обратно към метода main().

Списък, който се състои от текущия метод и метода, който го е извикал, и метода, който е извикал този и т.н., се нарича проследяване на стека . Можете да го получите с това изявление:

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

Можете също да го напишете като два реда:

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

Статичният currentThread()метод на Threadкласа връща препратка към Threadобект, който съдържа информация за текущата нишка, т.е. текущата нишка на изпълнение. Ще научите повече за нишките в нива 17 и 18 на мисията Java Core .

Този Threadобект има getStackTrace()метод, който връща масив от StackTraceElementобекти, всеки от които съдържа информация за метод. Взети заедно, всички тези елементи образуват проследяване на стека .

Пример:

Код
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);
   }
}
Конзолен изход
java.base/java.lang.Thread.getStackTrace(Thread.java:1606)
Main.test(Main.java:11)
Main.main(Main.java:5)

Както можем да видим в конзолния изход на примера, getStackTrace()методът върна масив от три елемента:

  • getStackTrace()метод на Threadкласа
  • test()метод на Mainкласа
  • main()метод на Mainкласа

От това проследяване на стека можем да заключим, че:

  • Методът Thread.getStackTrace()беше извикан от Main.test()метода на ред 11 на file Main.java
  • Методът Main.test()беше извикан от Main.main()метода на ред 5 на file Main.java
  • Никой не е извикал Main.main()метода — това е първият метод във веригата от извиквания.

Между другото, само част от наличната информация се показваше на екрана. Всичко останало може да се получи директно от StackTraceElementобекта



2.StackTraceElement

Както подсказва името му, StackTraceElementкласът е създаден, за да съхранява информация за елемент на проследяване на стека , т.е. един метод в stack trace.

Този клас има следните методи за екземпляри:

Метод Описание
String getClassName()
Връща името на класа
String getMethodName()
Връща името на метода
String getFileName()
Връща името на file (един файл може да съдържа няколко класа)
int getLineNumber()
Връща номера на реда във file, където е извикан методът
String getModuleName()
Връща името на модула (това може да бъде null)
String getModuleVersion()
Връща versionта на модула (това може да бъде null)

Те могат да ви помогнат да получите по-пълна информация за текущия стек от повиквания:

Код Конзолен изход Забележка
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
име на клас име
на метод име на
файл име
на ред номер
на модул име
на version version

на клас
име на метод име
на файл име
на ред номер на
модул име
на модул version име

на клас
име на метод име на
файл име
на ред номер на
модул име на
модул version


3. Стек

Вече знаете Howво е проследяване на стека , но Howво е стек (клас на стека)?

Стекът е структура от данни, към която можете да добавяте елементи и от която можете да извличате елементи. При това можете да вземете елементи само от края: първо вземате последния добавен, след това предпоследния добавен и т.н.

Самият стек с имена предполага това поведение, като How бихте взаимодействали с купчина documentи. Ако поставите листове 1, 2 и 3 в стек, трябва да ги извлечете в обратен ред: първо третия лист, след това втория и едва след това първия.

Java дори има специален клас за събиране на Stack със същото име и поведение. Този клас споделя много поведения с ArrayListи LinkedList. Но също така има методи, които прилагат поведението на стека:

Методи Описание
T push(T obj)
Добавя objелемента в горната част на стека
T pop()
Взема елемента от върха на стека (дълбочината на стека намалява)
T peek()
Връща елемента в горната част на стека (стекът не се променя)
boolean empty()
Проверява дали колекцията е празна
int search(Object obj)
Търси обект в колекцията и го връщаindex

Пример:

Код Съдържание на стека (горната част на стека е вдясно)
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]

Стековете се използват доста често в програмирането. Така че това е полезна колекция.



4. Показване на проследяване на стека по време на обработка на изключение

Защо списък с извиквания на методи се нарича проследяване на стека ? Защото, ако си представите списъка с методи като стек от листове хартия с имена на методи, тогава, когато извикате следващия метод, вие добавяте лист с името на този метод към стека. И следващият лист хартия отива отгоре и така нататък.

Когато даден метод приключи, листът в горната част на стека се премахва. Не можете да премахнете лист от средата на стека, без да премахнете всички листове над него. По същия начин не можете да прекратите метод в средата на верига от повиквания, без да прекратите всички методи, които е извикал.

Изключения

Друга интересна употреба на стекове е по време на обработка на изключения.

Когато възникне грешка в програма и бъде хвърлено изключение , изключението съдържа текущото проследяване на стекамасив, състоящ се от списък с методи, започвайки от основния метод и завършвайки с метода, при който е възникнала грешката. Има дори реда, където е хвърлено изключението!

Това проследяване на стека се съхранява в изключението и може лесно да бъде извлечено от него чрез следния метод:StackTraceElement[] getStackTrace()

Пример:

Код Забележка
try
{
   // An exception may occur here
}
catch(Exception e)
{
   StackTraceElement[] methods = e.getStackTrace()
}




Улавяне на изключението

Вземете проследяването на стека, което е съществувало, когато е възникнала грешката.

Това е метод на Throwableкласа, така че всички негови потомци (т.е. всички изключения) имат getStackTrace()метода. Супер удобно, а?

Показване на трасирането на стека на изключението

Между другото, Throwableкласът има друг метод за работа с проследяване на стека, метод, който показва цялата информация за проследяване на стека, съхранена в изключението. Нарича се printStackTrace().

Доста удобно, можете да го извикате по всяко изключение.

Пример:

Код
try
{
   // An exception may occur here
}
catch(Exception e)
{
   e.printStackTrace();
}
Конзолен изход
java.base/java.lang.Thread.getStackTrace(Thread.java:1606)
Main.test(Main.java:11)
Main.main(Main.java:5)