1. Отримання траси стеку
Мова програмування Java надає програмісту дуже багато способів отримати інформацію про те, що наразі відбувається в програмі. І це не просто слова.
Наприклад, програми мовою C++ після компіляції перетворюються на один великий файл машинного коду, і все, що під час виконання є доступним програмісту, — це адреса шматка пам'яті з машинним кодом, який зараз виконується. Негусто, еге ж?
А от у Java навіть після компіляції класи залишаються класами, методи й змінні нікуди не діваються, і програміст має багато способів отримати дані про те, що наразі відбувається в програмі.
Траса стеку
Наприклад, у будь-який момент роботи програми можна дізнатися клас та ім'я методу, який зараз виконується. І навіть не тільки отримати ім'я одного методу, а інформацію про весь ланцюжок викликів методів від поточного до методу main()
.
Список, який складається з поточного методу; методу, що його викликав; методу, що викликав цей другий метод і т. д., називається stack trace (траса стеку). Отримати його можна за допомогою команди
StackTraceElement[] methods = Thread.currentThread().getStackTrace();
Її також можна записати у два рядки:
Thread current = Thread.currentThread();
StackTraceElement[] methods = current.getStackTrace();
Статичний метод currentThread()
класу Thread
повертає посилання на об'єкт типу Thread
, який містить інформацію про поточну нитку (про поточний потік виконання). Докладніше про нитки ви дізнаєтеся на 7-му й 8-му рівнях квесту Java Core.
У цього об'єкта Thread
є метод getStackTrace()
, який повертає масив елементів StackTraceElement
, де кожен з елементів містить інформацію про один метод. Усі елементи разом і утворюють трасу стеку.
Приклад:
Код |
---|
|
Виведення на екран |
|
Як ми бачимо з виведення на екран, у наведеному прикладі метод getStackTrace()
повернув масив з трьох елементів:
- Метод
getStackTrace()
класуThread
- Метод
test()
класуMain
- Метод
main()
класуMain
З цієї траси стеку можна зробити такий висновок:
- Метод
Thread.getStackTrace()
було викликано з методуMain.test()
у рядку 11 файлу Main.java - Метод
Main.test()
було викликано з методуMain.main()
у рядку 5 файлу Main.java - Метод
Main.main()
ніхто не викликав — це перший метод у ланцюжку викликів.
До речі, на екрані було відображено лише частину всієї наявної інформації. Решту можна отримати безпосередньо з об'єкта StackTraceElement
2. StackTraceElement
Клас StackTraceElement
, як випливає з його назви, створено для зберігання інформації щодо одного елемента траси стеку — тобто щодо одного методу з траси стеку (StackTrace
).
Об'єкти цього класу мають такі методи:
Метод | Опис |
---|---|
|
Повертає ім'я класу |
|
Повертає ім'я методу |
|
Повертає ім'я файлу (в одному файлі може бути багато класів) |
|
Повертає номер рядка (у файлі), в якому був виклик методу |
|
Повертає ім'я модуля (може бути null ) |
|
Повертає версію модуля (може бути null ) |
За допомогою цих методів можна отримати повнішу інформацію щодо поточного стеку викликів:
Код | Виведення на екран | Примітка |
---|---|---|
|
|
ім'я класу ім'я методу ім'я файлу номер рядка ім'я модуля версія модуля ім'я класу ім'я методу ім'я файлу номер рядка ім'я модуля версія модуля ім'я класу ім'я методу ім'я файлу номер рядка ім'я модуля версія модуля |
3. Стек
Що таке Stack Trace (траса стеку), ви вже знаєте, а що ж таке власне Stack (стек)?
Стек — це структура зберігання даних, в яку можна додавати елементи і з якої можна їх забирати. Причому брати елементи можна лише з кінця: спочатку доданий останнім, потім — передостанній і т. д.
Сама назва Stack перекладається з англійської як «стіс», а стек дійсно дуже схожий на стіс паперу. Якщо ви покладете на стіс паперу аркуші 1, 2 і 3, узяти їх ви зможете лише в зворотному порядку: спочатку третій, потім другий, і тільки потім перший.
У Java навіть є спеціальна колекція з такою поведінкою і такою самою назвою — Stack. Цей клас своєю поведінкою дуже схожий на ArrayList
і LinkedList
. Натомість він ще має методи, що реалізують поведінку стеку:
Методи | Опис |
---|---|
|
Додає елемент obj у кінець списку (на верх стосу) |
|
Забирає елемент з верху стосу (висота стосу зменшується) |
|
Повертає елемент з верху стосу (стіс не змінюється) |
|
Перевіряє, чи не є колекція порожньою |
|
Шукає об'єкт із колекції, повертає його index |
Приклад:
Код | Вміст стеку (вершина — праворуч) |
---|---|
|
|
Стек використовується в програмуванні досить часто. Тож це корисна колекція.
4. Виведення траси стеку під час обробки помилок
Чому ж список викликів методів назвали StackTrace? А тому, що якщо уявити список методів у вигляді стосу аркушів з іменами методів, то під час виклику чергового методу на цей стіс кладеться аркуш з іменем методу, на нього — наступний і т. д.
Коли метод завершується, аркуш з верху стопки видаляється. Не можна видалити аркуш із середини стосу, не видаливши всі аркуші, які лежать на ньому, — не можна припинити роботу методу, не завершивши всі викликані ним методи в ланцюжку викликів.
Винятки
Ще одне цікаве застосування стеку — обробка винятків.
Коли в програмі виникає помилка і створюється виняток, у нього записується поточна stack trace: масив, який складається зі списку методів починаючи з методу main і закінчуючи методом, де сталася помилка. Там навіть є рядок, в якому було створено виняток!
Ця траса стеку для виявлення помилки зберігається всередині винятку, і її можна легко витягти звідти за допомогою методу StackTraceElement[] getStackTrace()
Приклад:
Код | Примітка |
---|---|
|
Захоплюємо виняток Отримуємо з нього трасу стеку на момент виникнення помилки. |
Це метод класу Throwable
, а отже, усі його класи-спадкоємці (тобто взагалі всі винятки) мають метод getStackTrace()
. Дуже зручно, чи не так?
Друк траси стеку для виявлення помилки
До речі, клас Throwable
має ще один метод для роботи зі stack trace: він виводить у консоль усю інформацію щодо траси стеку, яка зберігається всередині винятку. Цей метод так і називається: printStackTrace()
.
Викликати його можна для будь-якого винятку, що дуже зручно.
Приклад:
Код |
---|
|
Виведення на екран |
|
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ