IvanDurov
25 уровень

Стек-трейс Java

Статья из группы Архив info.javarush
участников
Виртуальная Машина Java (в дальнейшем JVM) обрабатывая код, запускает методы один за другим, начиная с метода main. Когда она доходит до очередного метода, говорится, что этот метод находится на вершине стека. После полного выполнения метода, он удаляется из стека, и сменяется следующим в очереди. Для демонстрации принципа, наберите данный код: Стек-трейс Java - 1
package errorhandling;

public class errorChecking {
    public static void main(String[] args) {
        System.out.println("Метод Main успешно запущен");
        m1();
        System.out.println("Метод Main заканчивает свою работу");
    }

    static void m1() {
        System.out.println("Первый метод передаёт привет!(m1)");
        m2();
    }

    static void m2() {
        System.out.println("Второй метод передаёт привет(m2)");
    }
}
У нас три метода: метод main, метод m1 и метод m2. Когда программа стартует, на вершине стека расположен метод main. Внутри метода main, вызывается метод m1. Вызываясь, он прыгает на верхушку стека. Метод m1 в свою очередь вызывает метод m2. Теперь уже метод m2, прыгает на верхушку стека, временно отстраняя m1. На секунду представьте это — main, сверху m1 и на вершине m2! Сделав свои дела, m2 завершается, и контроль возвращается обратно к m1. Метод m1, завершаясь, тоже удаляется из стека, и управление вновь получает метод main. Запустите вашу программу и посмотрите на окно вывода: Метод Main успешно запущен Первый метод передаёт привет!(m1) Второй метод передаёт привет(m2) Метод Main заканчивает свою работу Если что-то пойдёт не так в методе m2, JVM (Виртуальная Машина Джава, вы помните, да?) будет искать обработчиков ошибок, например блок try … catch. Если в методе m1 обработчика ошибок нет, то исключение будет передано методу m1, в надежде, что он сумеет её обработать. Если и здесь не обнаружит обработчика ошибок,то исключение снова перейдёт по стеку вверх, на этот раз в метод main. Если метод main не взаимодействует с исключением, вы получите странное сообщение об ошибке, напечатанное в окне вывода. В качестве примера, приведите ваш метод m2 к следующему виду:
static void m2() {
    int x = 10;
    int y = 0;
    double z = x / y;
    System.out.println( z );
    System.out.println("Method Two - m2");
}
Этот метод содержит ошибку деления на ноль. А вот полный вариант программы, сверьтесь со своим:
package errorhandling;

public class errorChecking {
    public static void main(String[] args) {
        System.out.println("Метод Main успешно запущен");
        m1();
        System.out.println("Метод Main заканчивает свою работу");
    }

    static void m1() {
        System.out.println("Первый метод передаёт привет!(m1)");
        m2();
    }

    static void m2() {
        int x = 10;
        int y = 0;
        double z = x / y;
        System.out.println( z );
        System.out.println("Method Two - m2");
    }
}
Запустите программу и посмотрите, что выдаст вам окно вывода: Метод Main успешно запущен Первый метод передаёт привет!(m1) Exception in thread "main" java.lang.ArithmeticException: / by zero at errorhandling.errorChecking.m2(<u>errorChecking.java:17</u>) at errorhandling.errorChecking.m1(<u>Solution.java:11</u>) at errorhandling.errorChecking.main(<u>>Solution.java:5</u>) Process finished with exit code 1 Вы смотрите на нечто, называемое стек-трейс. Три строки, подчёркнутые голубым, ссылаются на ваши методы, и могут быть найдены в: имя_пакета.имя_класса.имя_метода Первая сверху строка — это место, где ошибка возникла — в методе m2. Java проследила, что бы она была обработана ArithmeticException, которая вылавливает ошибки деления на ноль. В методах m2, m1 и main нет обработчика ошибок. Так что программа обработала её обработчиком ошибок по-умолчанию. Измените ваш метод m1 на следующий:
try {
    System.out.println("Первый метод передаёт привет!(m1)");
    m2( );
}
catch (ArithmeticException err) {
    System.out.println(err.getMessage());
}
Теперь мы обернули метод m2 в блок try. В части catch, мы используем тип исключения, что был выявлен в стек-трейсе — ArithmeticException. Запустите код снова, и в окне вывода увидите следующее: Метод Main успешно запущен Первый метод передаёт привет!(m1) / by zero Метод Main заканчивает свою работу Заметьте, что сообщение об ошибке вывелось как: "/ by zero". Метод m2 не был выполнен полностью, а был остановлен, когда возникла ошибка. Затем контроль был передан обратно m1. Это произошло благодаря тому, что блок catch сам распознавал ошибку, JVM не стала обращаться к стандартному обработчику ошибок, а вывела сообщение находящееся между фигурными скобками блока catch. Обратите внимание, что сама программа не была остановлена. Контроль, как обычно перешёл к методу main, откуда m1 был вызван. И последняя строка метода main, таки смогла вывести на экран "End Main method". Это имеет очень-очень важное значение. Если бы вам нужно было значение из m1, для последующей работы где-то в main. И если значения там не окажется, то ваша программа может отработать совсем не так, как вы ожидаете. Когда вы увидите стек-трейс в окне вывода, просто знайте, что первая строка — это то место, где проблема возникла, а остальные строки (если конечно они есть), куда исключение было передано вверх по стеку, обычно заканчивая методом main. Перевод с сайта homeandlearn.co.uk Говорим спасибо: Серегею Сысоеву, Treefeed...
Комментарии (12)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Artem Sokolov Android Developer в Oracle
29 июня 2022, 07:01
Здесь похоже опечатка: "Если в методе m1 обработчика ошибок нет, то исключение будет передано методу m1, в надежде, что он сумеет её обработать. "
hidden #2595317
Уровень 45
26 мая 2021, 14:48
Если в методе m1 обработчика ошибок нет, то исключение будет передано методу m1, в надежде, что он сумеет её обработать/ Исправьте м1 первый на м2.
🦔 Виктор веду учебный тг-канал в t.me/Javangelion Expert
5 ноября 2020, 14:34
Спасибо за перевод, доступна, легкая и понятная выжимка по стековому пути. Забираю в свой список статей по этой теме, не смотря на то, что перевод лежит в архиве от 2013 года. Можно на эту тему ещё почитать следующие статьи: Структуры данных — стек и очередь. Stack Trace и с чем его едят. p. s. Очень интересно, как работает редактирование статьи на ДжаваРаше, потому что очень много статей, вне лекций, написаны с банальными опечатками, дублями и висят в таком виде годами и на эти материалы ссылаются официальные лекции курса ( ! ). Не хочу быть занудой, но создаётся ощущение, что либо редактировать статьи после публикации нельзя, либо неужели авторам настолько безразлична дальнейшая судьба работы?..
Bogdan
Уровень 19
21 июля 2020, 12:36
public class errorChecking Имена классов и интерфейсов начинаются с большой буквы и пишутся в стиле CamelCase. Имена методов и переменных начинаются с маленькой буквы.
Гвазава Сергей
Уровень 31
24 февраля 2020, 08:16
Когда вы увидите стек-трейс в окне вывода, просто знайте, что первая строка — это то место, где проблема возникла, а остальные строки (если конечно они есть), куда исключение было передано вверх по стеку, обычно заканчивая методом main. (я могу ошибаться, но наверно вниз по стеку? если последующие методы кладутся сверху, то метод MAIN будет внизу стека)
namor
Уровень 1
20 января 2020, 11:31
"если в методе m1 обработчика ошибок нет, то исключение будет передано методу m1" и так и передавал он его сам себе 30 лет и три года....
Anonymous #2135740
Уровень 35
5 октября 2019, 14:13
"Если в методе m1 обработчика ошибок нет, то исключение будет передано методу m1, в надежде, что он сумеет её обработать". Предполагаю, что допущена опечатка и первый метод нужно указать, как m2?
BariO
Уровень 19
5 сентября 2019, 05:11
спасибо за внесение еще большей ясности
Дмитрий
Уровень 22
22 августа 2019, 07:06
Я так понял: Стек это стек FIFO, получается при отработки try / catch JVM ищет нужное исключение так же в других методотах стека где в недрах JVM и только когда она нашла нужный она помещает вызов этого исключения на вершину стека с которым мы и работаем в отработку исключения.
Игорь HDL developer в Y
27 января 2020, 08:48
Не FIFO, а LIFO
hundr3th
Уровень 33
14 марта 2015, 11:25
Спасибо, но было бы еще удобнее, если метод1 работал бы как мэин, т.е. после выполнения метода2 выводилось бы сообщение, что метод1 завершил работу с:
Naissur
Уровень 40
20 августа 2015, 11:06
Зато есть возможность поэкспериментировать, добавить что-то свое, например, так:
public class ErrorChecking {
	public static void main(String[] args) {
		System.out.println("Метод Main успешно запущен");
		m1();
		System.out.println("Метод Main заканчивает свою работу");
	}
	
	static void m1() {
		try {
			System.out.println("Первый метод передает привет!(m1)");
			m2();
		} catch (ArithmeticException err) {
			System.out.println(err.getMessage());
			System.out.println("Второй метод завершился некорректно");
		} finally {
			System.out.println("Первый метод заканчивает свою работу");
		}
	}
	
	static void m2() {
		System.out.println("Второй метод передает привет!(m2)");
		int x = 10;
		int y = 0;
		double z = x / y;
		System.out.println(z);
		System.out.println("Второй метод заканчивает свою работу");
	}
}