Stack trace (трассировка стека вызовов) в Java это список методов, через которые прошла программа до текущей точки или до момента ошибки. Он работает по принципу стека LIFO (последний вызванный метод на вершине и печатается первым), поэтому по нему видно всю цепочку вызовов от места ошибки вниз до метода
main: класс, метод, файл и номер строки.
В этой статье вы узнаете и поймете, как работает такое явление в Java, как StackTrace, так же известное как "Трассировка стека вызовов". Эта информация была структурирована для новичков, столкнувшихся с этим понятием в начале девятого уровня Java Syntax.Кратко
- Stack trace (трассировка стека) это список методов, через которые прошла программа до текущей точки или до ошибки.
- Он работает по принципу стека LIFO (Last In First Out): последний вызванный метод оказывается на вершине и печатается первым.
- При исключении JVM печатает stack trace: тип ошибки, затем цепочку вызовов от места ошибки вниз до
main.
- Каждая строка трассировки показывает класс, метод, файл и номер строки.
- Получить стек в коде можно так:
Thread.currentThread().getStackTrace(), вернется массив StackTraceElement[].
- У
StackTraceElement есть методы getClassName(), getMethodName(), getFileName(), getLineNumber().
Я думаю все из вас, хоть раз, но встречали похожие ошибки при работе в вашем IDE, независимо от того будь это
Idea,
Eclipse или что-то другое.
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 работает как
Стэк и это видно из его названия. На этом месте мы остановимся чуть поподробнее.
Как работает стек (LIFO)
![Принцип работы стека Stack: LIFO, элементы кладутся и берутся с вершины]()
На восьмом уровне вы уже познакомились с коллекциями и знаете что они делятся на три группы
Set это множество,
List это список,
Map это словарь (или карта).
По мнению JavaRush (c).
Наш
Stack входит в группу
List. Принцип его работы можно описать как
LIFO, что расшифровывается как
Last In First Out(Последний пришел, первый ушел). А именно это такой список похожий на стопку книг, чтобы взять элемент который мы положили в
Stack первым, нам необходимо сначала извлечь все элементы которые мы добавили в наш список после. Как это указано на картинке выше в отличии например от обычного списка
ArrayList где мы можем получить любой элемент из списка по индексу.
Еще раз для закрепления. Получение элемента из Стэка возможно только с конца! В то время как первый добавленный в него элемент находится в начале(или на дне как удобнее).
Вот какие методы имеет наш
Stack:
| Метод | Что делает |
push() | добавляет элемент на вершину стека |
pop() | возвращает верхний элемент и удаляет его |
peek() | возвращает верхний элемент, но не удаляет его |
search() | ищет элемент; возвращает его смещение от вершины или -1 |
empty() | возвращает true, если стек пуст, иначе false |
Stack Trace при ошибке
Так для чего же в
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 на примере) и мы видим что проблема возникла в классе
Integer на 614 строке кода и вызвала эту строку, строка 770 метода
parseInt того же класса(№3 на примере) которая при добавлении в
Стэк была под номером три и этот метод класса
Integer все еще не видимый нам был вызван уже нашим методом
convertStringToInt располагающемся на 10 строке нашей программы(№4 на примере, а при добавлении он был вторым), а его в свою очередь вызвал
main на 6 строке(№5 на примере, а при добавлении соответственно первый).
Вот так вот, складируя в
Стек шаг за шагом наши вызываемые методы мы смогли вернуться обратно в
main параллельно печатая информацию что именно привело нас к ошибке.
Как получить stack trace в коде
Но
StackTrace это не только работа с ошибками, он позволяет получить нам кучу интересной информации о процессе работы нашего приложения. Давайте разберем еще один популярный пример в комментариях к основной лекции 9го уровня. У нас есть код и к нему сразу прикреплю картинку визуализирующую процесс работы программы:
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());
}
}
}
![Порядок вызова методов и заполнение stack trace от main к method5]()
Тут наша программа безошибочно выполняет свою работу и заканчивается. Вот что мы увидим в выводе консоли:
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
Вот что еще мы можем получить из
StackTraceElement:
| Метод | Что возвращает |
getClassName() | имя класса |
getMethodName() | имя метода |
getFileName() | имя файла (в одном файле может быть много классов) |
getModuleName() | имя модуля (может быть null) |
getModuleVersion() | версию модуля (может быть null) |
getLineNumber() | номер строки в файле, в которой был вызов метода |
Теперь, когда вы поняли общий принцип работы, советую вам самим опробовать разные методы StackTrace в вашей Ide. Даже если вы не совсем все усвоили, продолжайте обучение и мозаика сложится так же как сложилась у меня в данном вопросе. Желаю вам всем успехов!
P.s. Если вам понравился данный материал, пожалуйста поддержите лайком. Вам не трудно, мне приятно. Спасибо и увидимся на 41 уровне ;)
Полное описание класса есть в
документации Oracle (StackTraceElement).
Читайте также
- Классы-обертки: метод
Integer.parseInt() из примера и ошибка NumberFormatException.
- Класс ArrayList: обычный список, где, в отличие от стека, доступ к элементам идет по индексу.
Вопросы и ответы
Что такое stack trace в Java?
Это трассировка стека вызовов: список методов, через которые прошла программа до текущей точки или до момента ошибки. Помогает понять, где и почему возникло исключение.
Как читать stack trace?
Сверху печатается тип ошибки, ниже цепочка вызовов от места, где ошибка возникла, вниз до метода
main. Каждая строка показывает класс, метод, файл и номер строки.
Почему stack trace работает по принципу LIFO?
Потому что вызовы методов складываются в стек: последний вызванный метод оказывается на вершине. При ошибке он печатается первым, а
main (вызванный самым первым) последним.
Как получить stack trace программно?
Вызвать
Thread.currentThread().getStackTrace(); метод вернет массив
StackTraceElement[] с информацией о выполненных методах.
Какую информацию дает StackTraceElement?
Имя класса (
getClassName), имя метода (
getMethodName), имя файла (
getFileName), номер строки (
getLineNumber) и данные о модуле.
Почему в примере первым элементом массива оказался getStackTrace?
Потому что в момент вызова
getStackTrace() он был последним выполненным методом и по принципу LIFO попал на вершину стека, то есть в нулевой элемент массива.