Иногда, когда я запускаю свое приложение, оно выдает мне ошибку, которая выглядит следующим образом:
Exception in thread "main" java.lang.NullPointerException
at com.example.myproject.Book.getTitle(Book.java:16)
at com.example.myproject.Author.getBookTitles(Author.java:25)
at com.example.myproject.Bootstrap.main(Bootstrap.java:14)
Люди называют это «следом стека». Что такое трассировка стека? Что он может сказать мне об ошибке, которая происходит в моей программе?
Об этом вопросе - довольно часто я вижу вопрос, когда начинающий программист «получает ошибку», и они просто вставляют свою трассировку стека и некоторый случайный блок кода, не понимая, что такое трассировка стека или как они могут ее использовать. Этот вопрос предназначен для начинающих программистов, которым может понадобиться помощь в понимании значения трассировки стека.
java
debugging
stack-trace
Роб Хруска
источник
источник
Ответы:
Проще говоря, трассировка стека - это список вызовов методов, которые приложение находилось в середине, когда было сгенерировано исключение.
Простой пример
С помощью примера, приведенного в вопросе, мы можем точно определить, где было выброшено исключение в приложении. Давайте посмотрим на трассировку стека:
Это очень простая трассировка стека. Если мы начнем с начала списка «at ...», мы можем сказать, где произошла наша ошибка. Мы ищем самый верхний вызов метода, который является частью нашего приложения. В данном случае это:
Чтобы отладить это, мы можем открыть
Book.java
и посмотреть на строку16
, которая:Это будет означать, что что-то (вероятно
title
) находитсяnull
в приведенном выше коде.Пример с цепочкой исключений
Иногда приложения перехватывают исключение и повторно генерируют его как причину другого исключения. Это обычно выглядит так:
Это может дать вам трассировку стека, которая выглядит следующим образом:
То, что отличается от этого, это «Причины». Иногда исключения будут иметь несколько разделов «Причины». Для них обычно требуется найти «основную причину», которая будет одной из самых низких «причинных» секций в трассировке стека. В нашем случае это:
Опять же , с этим исключением мы хотели бы посмотреть на линию
22
из ,Book.java
чтобы увидеть , что может привестиNullPointerException
здесь.Более сложный пример с библиотечным кодом
Обычно трассировки стека намного сложнее, чем два приведенных выше примера. Вот пример (он длинный, но демонстрирует несколько уровней связанных исключений):
В этом примере намного больше. Что нас больше всего беспокоит, так это поиск методов, взятых из нашего кода , которые будут в
com.example.myproject
пакете. Из второго примера (выше), мы сначала хотели бы найти первопричину, а именно:Однако все вызовы методов в соответствии с этим являются библиотечным кодом. Итак, мы перейдем к «Причины» над ним и поищем первый вызов метода, происходящий из нашего кода, а именно:
Как и в предыдущих примерах, мы должны смотреть
MyEntityService.java
онлайн59
, потому что именно здесь возникла эта ошибка (эта ошибка немного очевидна, что пошло не так, поскольку SQLException сообщает об ошибке, но мы ищем процедуру отладки).источник
Exception in thread "main"
вашего первого примера. Я думаю, что было бы особенно полезно объяснить, что эта строка часто сопровождается сообщением, таким как значение переменной, которое может помочь диагностировать проблему. Я попытался внести изменения самостоятельно, но я изо всех сил стараюсь вписать эти идеи в существующую структуру вашего ответа.Я публикую этот ответ, поэтому самый верхний ответ (при сортировке по активности) не является просто ошибочным.
Что такое Stacktrace?
Stacktrace - очень полезный инструмент отладки. Он показывает стек вызовов (т. Е. Стек функций, которые были вызваны до этой точки) в момент возникновения неперехваченного исключения (или в момент, когда трассировка стека была сгенерирована вручную). Это очень полезно, поскольку показывает не только, где произошла ошибка, но и то, как программа оказалась в этом месте кода. Это приводит к следующему вопросу:
Что такое исключение?
Исключением является то, что среда выполнения использует, чтобы сообщить вам, что произошла ошибка. Популярные примеры: NullPointerException, IndexOutOfBoundsException или ArithmeticException. Каждый из них вызывается, когда вы пытаетесь сделать что-то, что невозможно. Например, исключение NullPointerException будет выдано при попытке разыменования объекта Null:
Как я должен иметь дело со стеками / исключениями?
Сначала выясните, что вызывает исключение. Попробуйте поискать название исключения, чтобы выяснить причину этого исключения. В большинстве случаев это будет вызвано неправильным кодом. В приведенных выше примерах все исключения вызваны неправильным кодом. Так что для примера NullPointerException вы можете убедиться, что
a
он никогда не будет нулевым в то время. Вы можете, например, инициализироватьa
или включить проверку, подобную этой:Таким образом, оскорбительная строка не выполняется, если
a==null
. То же самое касается других примеров.Иногда вы не можете быть уверены, что не получите исключения. Например, если вы используете сетевое соединение в своей программе, вы не можете остановить компьютер от потери его интернет-соединения (например, вы не можете запретить пользователю отключать сетевое соединение компьютера). В этом случае сетевая библиотека, вероятно, выдаст исключение. Теперь вы должны поймать исключение и обработать его. Это означает, что в примере с сетевым подключением вы должны попытаться повторно открыть подключение или уведомить пользователя или что-то в этом роде. Кроме того, всякий раз, когда вы используете catch, всегда перехватываете только исключение, которое вы хотите перехватить, не используйте такие широкие операторы catch, как
catch (Exception e)
что бы поймать все исключения. Это очень важно, потому что в противном случае вы можете случайно поймать не то исключение и отреагировать неправильно.Почему я не должен использовать
catch (Exception e)
?Давайте используем небольшой пример, чтобы показать, почему вы не должны просто перехватывать все исключения:
Этот код пытается отловить
ArithmeticException
причину, вызванную возможным делением, на 0. Но он также отлавливает возможноеNullPointerException
, которое выбрасывается, еслиa
илиb
естьnull
. Это означает, что вы можете получить,NullPointerException
но вы будете относиться к нему как к ArithmeticException и, вероятно, будете поступать неправильно. В лучшем случае вы все еще скучаете по тому, что было исключение NullPointerException. Такие вещи делают отладку намного сложнее, так что не делайте этого.TLDR
Если 1. невозможно, перехватите конкретное исключение и обработайте его.
catch (Exception e)
, всегда ловите определенные исключения. Это избавит вас от многих головных болей.источник
Чтобы добавить к тому, что Роб упомянул. Установка точек останова в вашем приложении позволяет выполнять пошаговую обработку стека. Это позволяет разработчику использовать отладчик, чтобы увидеть, в какой момент метод делает что-то непредвиденное.
Поскольку Роб использовал
NullPointerException
(NPE) для иллюстрации чего-то общего, мы можем помочь устранить эту проблему следующим образом:если у нас есть метод, который принимает параметры, такие как:
void (String firstName)
В нашем коде мы хотели бы оценить, что
firstName
содержит значение, мы сделали бы это так:if(firstName == null || firstName.equals("")) return;
Вышесказанное мешает нам использовать
firstName
в качестве небезопасного параметра. Поэтому, выполняя нулевые проверки перед обработкой, мы можем помочь гарантировать, что наш код будет работать правильно. Чтобы расширить пример, который использует объект с методами, мы можем посмотреть здесь:if(dog == null || dog.firstName == null) return;
Выше приведен правильный порядок проверки на нулевые значения, мы начинаем с базового объекта, в данном случае с собакой, а затем начинаем идти по дереву возможностей, чтобы убедиться, что все верно перед обработкой. Если бы порядок был отменен, NPE потенциально мог бы быть брошен, и наша программа потерпела бы крах.
источник
null
когдаNullPointerException
проверяется.Существует еще одна функция трассировки стека, предлагаемая семейством Throwable - возможность манипулировать информацией трассировки стека.
Стандартное поведение:
Трассировки стека:
Трассировка манипулируемого стека:
Трассировки стека:
источник
Чтобы понять имя : трассировка стека - это список Исключений (или вы можете сказать список «Причины»), от самого поверхностного Исключения (например, Исключение сервисного уровня) до самого глубокого (например, Исключение базы данных). Точно так же, как причина, по которой мы называем это «стеком», заключается в том, что стек является первым в последнем выходе (FILO), самое глубокое исключение произошло в самом начале, затем была создана цепочка исключений с серией последствий, исключение на поверхности было последним одно произошло вовремя, но мы видим это в первую очередь.
Ключ 1 : хитрая и важная вещь, которую необходимо здесь понять: самая глубокая причина не может быть «первопричиной», потому что если вы пишете какой-то «плохой код», это может вызвать некое исключение под ним, которое глубже, чем его слой. Например, неверный SQL-запрос может привести к сбросу соединения SQLServerException в нижней части окна, а не к ошибке sinax, которая может быть только в середине стека.
-> Найти основную причину в середине ваша работа.
Ключ 2 : Еще одна хитрая, но важная вещь находится внутри каждого блока «Причинить», первая строка была самым глубоким слоем и была первой для этого блока. Например,
Book.java:16 был вызван Auther.java:25, который был вызван Bootstrap.java:14, Book.java:16 был основной причиной. Здесь прикрепите диаграмму сортировки стека трасс в хронологическом порядке.
источник
Просто чтобы добавить к другим примерам, есть внутренние (вложенные) классы, которые появляются со
$
знаком. Например:Результатом будет следующая трассировка стека:
источник
В других статьях описывается, что такое трассировка стека, но с ней все еще сложно работать.
Если вы получаете трассировку стека и хотите отследить причину исключения, хорошей отправной точкой для понимания этого является использование консоли Java Stack Trace Console в Eclipse . Если вы используете другую IDE, может быть похожая функция, но этот ответ касается Eclipse.
Во-первых, убедитесь, что у вас есть все источники Java, доступные в проекте Eclipse.
Затем в перспективе Java нажмите на вкладку Консоль (обычно внизу). Если вид консоли не отображается, перейдите к пункту меню « Окно» -> «Показать вид» и выберите « Консоль» .
Затем в окне консоли нажмите на следующую кнопку (справа)
а затем выберите консоль Java Stack Trace из раскрывающегося списка.
Вставьте трассировку стека в консоль. Затем он предоставит список ссылок на ваш исходный код и любой другой доступный исходный код.
Вот что вы можете увидеть (изображение из документации по Eclipse):
Самый последний вызов метода будет вершиной стека, которая является верхней строкой (исключая текст сообщения). Спуск по стеку уходит в прошлое. Вторая строка - это метод, который вызывает первую строку и т. Д.
Если вы используете программное обеспечение с открытым исходным кодом, вам может потребоваться загрузить и прикрепить к своему проекту источники, если вы хотите проверить. Загрузите jar- файлы с исходным кодом, в своем проекте откройте папку Referenced Libraries, чтобы найти jar-файл для вашего модуля с открытым исходным кодом (тот, который содержит файлы классов), затем щелкните правой кнопкой мыши, выберите Properties и прикрепите jar- файл с исходным кодом.
источник