У цій статті ви дізнаєтеся і зрозумієте, як працює таке явище в Java, як StackTrace, також відоме як "Трасування стека викликів". Ця інформація була структурована для новачків, які зіткнулися з цим поняттям на початку дев'ятого рівня Java Syntax.
Я думаю всі з вас, хоч раз, але зустрічали схожі помилки під час роботи у вашому IDE, незалежно від того, чи то Idea, Eclipse, чи щось інше.
Integer на 614 рядку коду і викликала цей рядок, рядок 770 методу parseInt того самого класу (№3 в прикладі), який під час додавання в Стек був під номером три, і цей метод класу Integer, який ми досі не бачимо, був викликаний вже нашим методом convertStringToInt, який розміщено на 10 рядку нашої програми (№4 в прикладі, а під час додавання він був другим), а його, зі свого боку, викликав main на 6 рядку (№5 в прикладі, а під час додавання, відповідно, перший).
Ось так, складаючи в Стек крок за кроком наші викликані методи, ми змогли повернутися назад у main, паралельно друкуючи інформацію про те, що саме призвело до виникнення помилки.
Але StackTrace це не тільки робота з помилками, він дає змогу отримати нам купу цікавої інформації про процес роботи нашого застосунку. Давайте розберемо ще один популярний приклад у коментарях до основної лекції 9го рівня. У нас є код і до нього відразу додам малюнок, що візуалізує процес роботи програми:
Exception in thread "main" java.lang.ArithmeticException
at com.example.task01.Test.division(Test.java:10)
at com.example.task01.Test.main(Test.java:6)
Це, як ви вже здогадалися, і є наше трасування. Але не поспішайте панікувати, зараз ми з вами розкладемо цей приклад на пальцях. Спочатку необхідно зрозуміти той факт, що StackTrace працює як Стек і це видно з його назви. На цьому місці ми зупинимося трохи детальніше.
На восьмому рівні ви вже познайомилися з колекціями і знаєте, що вони поділяються на три групи Set – множина, List – список, Map – словник (або карта). На думку JavaRush (c).
Наш Stack є частиною групи List. Принцип його роботи можна описати як LIFO, що розшифровується як Last In First Out(Останній прийшов, перший пішов). А саме такий список схожий на стіс книжок, тому щоб узяти елемент, який ми поклали в Stack першим, нам необхідно спочатку витягти всі елементи, які ми додали в наш список після. Як це зазначено на малюнку вище, на відміну, наприклад, від звичайного списку ArrayList, де ми можемо отримати будь-який елемент зі списку за індексом.
Ще раз для закріплення. Отримання елемента зі Стека можливе тільки з кінця! Тоді як перший доданий у нього елемент перебуває на початку (або на дні, як зручніше).
Ось які методи має наш Stack:
Object push() – додає елемент на верх стека.
Object pop() – повертає елемент, що знаходиться у верхній частині стека, видаляючи його в процесі.
Object peek() – повертає елемент, що знаходиться у верхній частині стека, але не видаляє його.
int search() – шукає елемент у стеку. Якщо знайдено, повертається його зміщення від вершини стека. Інакше повертається -1.
boolean empty() – перевіряє, чи є стек порожнім. Повертає true, якщо стек порожній. Повертає false, якщо стек містить елементи.
Так для чого ж у Java потрібен StackTrace побудований на принципах роботи Stack? Давайте розберемо приклад помилки нижче, яка виникла в процесі виконання такої ось простої програми.
public class Test {
public static void main(String[] args) {
System.out.println(convertStringToInt(null));
}
public static int convertStringToInt(String s) {
int x = Integer.parseInt(s);
return x;
}
}
У нас є клас Test із двома методами. Усім звичний main і convertStringToInt, логіка якого полягає в конвертуванні та поверненні отриманого ззовні (а саме з методу main) рядка в цілочисельне число типу int. Як ви бачите, ми навмисно передали замість рядка з якоюсь цифрою параметр null. Цей параметр наш метод не зміг правильно обробити, що призвело до помилки NumberFormatException.
Як ви знаєте, програма починає відпрацьовувати свою роботу з методу main, і в цей момент вона створює новий Стек із назвою StackTrace, куди кладе поточне значення її роботи під номером 1, далі ми переходимо до методу convertStringToInt, і програма знову заводить параметри нашого знаходження у створений раніше StackTrace під номером 2, далі викликається невидимий нашому оку метод parseInt, що міститься в класі Integer, і це вже буде елемент під номером 3 нашого StackTrace, у цьому методі буде ще один внутрішній виклик, доданий до StackTrace під номером 4, для перевірки елемента на null, що й призведе до виникнення помилки.
Програмі необхідно вивести нашу помилку із зазначенням всього ланцюжка наших переходів до моменту виникнення помилки. Тут їй і приходить на допомогу раніше створений StackTrace з даними наших переходів.
Exception in thread "main" java.lang.NumberFormatException: null
at java.base/java.lang.Integer.parseInt(Integer.java:614)
at java.base/java.lang.Integer.parseInt(Integer.java:770)
at com.example.task01.Test.convertStringToInt(Solution.java:10)
at com.example.task01.Test.main(Solution.java:6)
До виникнення помилки, програма йшла вглиб методів, але щойно виникла помилка, все починає відбуватися у зворотному порядку. Друкується рядок з описом проблеми (№1 в прикладі), далі береться останнє (і те, що перебуває на вершині) додане значення до нашого Стеку, воно було під номером чотири, і друкується в консоль (№2 в прикладі), і ми бачимо, що проблема виникла в класі public class Test {
public static void main(String[] args) {
method1();
method2();
}
public static void method1() {
//не викликає нічого
}
public static void method2() {
method3();
method4();
}
public static void method3() {
//не викликає нічого
}
public static void method4() {
method5();
}
public static void method5() {
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
for (StackTraceElement element:stackTraceElements) {
System.out.println(element.getMethodName());
}
}
}
Тут наша програма безпомилково виконує свою роботу і завершується. Ось що ми побачимо у виведенні консолі:
getStackTrace
method5
method4
method2
main
Process finished with exit code 0
Як ми отримали таке виведення і що ж сталося в п'ятому методі починаючи з 20-го рядка? Боюся, найкраще, що я зможу зробити, це додати найпопулярніше пояснення (скорочено) юзера Кирила з коментарів до лекції.
Звернемося до рядка створення StackTrace і розберемо його поелементно:
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement[] – вказівка на тип масиву (На ранніх рівнях ви вже проходили масиви типу int[], String[], ось тут те ж саме).
stackTraceElements – ім'я масиву, може бути будь-яким з урахуванням загальних правил іменування, на роботу це не впливає.
Thread.currentThread() – отримання посилання на поточний потік, в якому виконуються методи, які ми хочемо відстежити (поки це не важливо, докладніше потоки ви будете розбирати на 16 рівні в квесті Java Core)
getStackTrace() – отримуємо весь Стек методів, що викликаються (це звичайний геттер для StackTrace)
Тепер подивимося, чим нам може бути корисний створений масив. Ми розуміємо, що в масиві зберігається інфа про виконані методи.
І для цього в 21-му рядку ми запускаємо модифікований цикл for під назвою forEach (до речі, хто ще не вивчив цей цикл, раджу почитати про нього) і виводимо дані з масиву в консоль, а саме інформацію про те, які методи були виконані в процесі роботи за допомогою конструкції element.getMethodName().
Увага, як ми бачимо, нульовим елементом масиву в нас виявився сам getStackTrace() відповідно, оскільки в момент отримання масиву даних він був останнім методом, що виконався, і через це опинився на верхівці Стека, а пам'ятаючи про нашу конструкцію
"Останній прийшов, перший пішов", він одразу ж першим додається в масив під нульовим елементом.
Ось що ще ми можемо отримати зі StackTraceElement:
String getClassName() – повертає ім'я класу.
String getMethodName() – повертає ім'я методу.
String getFileName() – повертає ім'я файлу (в одному файлі може бути багато класів).
String getModuleName() – повертає ім'я модуля (може бути null).
String getModuleVersion() – повертає версію модуля (може бути null).
int getLineNumber() – повертає номер рядка у файлі, в якому був виклик методу.
Тепер, коли ви зрозуміли загальний принцип роботи, раджу вам самим випробувати різні методи StackTrace у вашому Idea. Навіть якщо ви не зовсім усе засвоїли, продовжуйте навчання і мозаїка складеться так само, як склалася в мене щодо цього питання. Бажаю вам усім успіхів!
P. S. Якщо вам сподобався цей матеріал, будь ласка, підтримайте лайком. Вам не важко, мені приємно. Спасибі і побачимося на 41 рівні ;)
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ