Вы действительно читаете сообщения об исключениях Java?

Java

Дана следующая информация об исключении:

java.lang.RuntimeException: level 2 exception
    at com.msh.demo.exceptionStack.Test.fun2(Test.java:17)
    at com.msh.demo.exceptionStack.Test.main(Test.java:24)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.io.IOException: level 1 exception
    at com.msh.demo.exceptionStack.Test.fun1(Test.java:10)
    at com.msh.demo.exceptionStack.Test.fun2(Test.java:15)
    ... 6 more

После стольких лет изучения Java вы действительно будете читать информацию об исключениях Java? Можете ли вы уточнить последовательность событий в процессе создания исключения?

Контент, который необходимо усвоить

Написать демонстрационный тест

Приведенная выше информация об исключении генерируется демонстрацией:

package com.msh.demo.exceptionStack;

import java.io.IOException;

/**
 * Created by monkeysayhi on 2017/10/1.
 */
public class Test {
  private void fun1() throws IOException {
    throw new IOException("level 1 exception");
  }

  private void fun2() {
    try {
      fun1();
    } catch (IOException e) {
        throw new RuntimeException("level 2 exception", e);
    }
  }


  public static void main(String[] args) {
    try {
      new Test().fun2();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

На этот раз я скопировал все содержимое файла, чтобы номера строк кода в статье соответствовали фактическим номерам строк один за другим.

Согласно приведенной выше информации об исключении, последовательность событий в процессе создания исключения следующая:

  1. В строке 10 Test.java выдается IOExceotion («исключение 1-го уровня») e1.
  2. Исключение e1 выбрасывается слой за слоем, пока не будет поймано в строке 15 Test.java.
  3. В строке 17 файла Test.java в соответствии с перехваченным исключением e1 выдается RuntimeException("исключение уровня 2", e1) e2
  4. Исключение e2 выбрасывается слой за слоем, пока не будет поймано в строке 24 Test.java.
  5. Других аномальных сведений в последующем нет.После необходимого фреймворка метод e2.printStackTrace() автоматически вызывается программой или активно пользователем

Как прочитать информацию об исключении

Итак, как прочитать информацию об исключении? Есть несколько вещей, о которых вам нужно знать:

  • Стек исключений печатается в порядке FILO.Исключение в нижней части содержимого печати генерируется первым, что постепенно приводит к возникновению исключения выше.. Исключение в верхней части печати — это последнее, которое было выброшено и не перехвачено. Считая сверху вниз,i+1Исключение составляет первыйiпричина, по которой было выбрано исключениеcause, начиная с «Вызвано».
  • Каждое исключение в стеке исключений состоит из имени исключения + подробной информации + пути.. Имя исключения начинается в начале строки (или сразу следует за «Вызвано»), за которым следуют подробности (для лучшей читаемости необходимо указать соответствующие подробности), начиная со следующей строки, пропуская вкладку, это путь A позиция в , одна позиция в строке.
  • Путь печатается в порядке FIFO, и позиция в верхней части содержимого печати является первой, которая передается исключением, и выбрасывается слой за слоем.. Самая ранняя переданная позиция — это позиция, в которой генерируется исключение, и отсюда может быть запущена обратная отладка; последующая позиция обычно является точкой входа для вызова метода, которую можно получить из стека методов, когда JVM перехватывает исключение . Для причины его печатный путь до тех пор, пока он не будет завернут в следующее исключение, а затем напечатано «... еще 6», что указывает на то, что причина является завернутым исключением, и после этого он также проходит через слой 6 позиций по слой, но эти местоположения дублируют путь, который является оберткой для исключения, поэтому они здесь опущены и напечатаны в пути, который является оберткой для исключения. Информация "...еще 6" не важна и может быть проигнорирована.

Теперь достаточно просто вернуться и прочитать информацию об исключении для примера?

Чтобы облегчить понимание, я описал структуру и элементы информации об исключении настолько просто, насколько это возможно, и могу внести некоторые недостатки. Чтение информации об исключениях — важный навык Java-программистов, и я надеюсь, что вы сможете усвоить его и забыть об этих длинных описаниях.

Если вы до сих пор не понимаете, предлагаю вам проследить процесс создания и печати исключения самостоятельно, просто используйте пример кода, это просто, но достаточно. Сложность в том, что исключения — это механизм, предоставляемый JVM, и нужно понимать реализацию JVM, а многие нативные методы вызываются внизу, и отслеживать нативный код не так удобно.

расширять

Почему иногда я вижу только имя исключения "java.lang.NullPointerException" в журнале, но не стек исключений

В информации об исключении из примера есть три элемента: имя исключения, подробная информация и путь.Детали и пути могут быть опущены из-за оптимизации JVM..

Это часто происходит в журнале серверных приложений.Поскольку одно и то же исключение было напечатано несколько раз, если одно и то же исключение продолжает печататься, JVM пропустит подробную информацию и очереди путей.Прокрутите вперед, чтобы найти полную информацию об исключении.

Brother Monkey уже сталкивался с этой проблемой при использовании сервера Yarn Timeline Server. Вы чувствуете это чувство? Блин, почему только имя исключения и нет стека исключений? Если нет стека исключений, как я могу узнать, где генерируется исключение? Не могу остановить онлайн-сервис, все зависит от лога!

Подобных случаев в сети много, напримерNullPointerException теряет информацию о стеке исключений, читатели могут обратиться к этой ссылке, чтобы поэкспериментировать.

Как добавить переменную-член в класс исключений

Чтобы правильно выразить исключение, нам иногда нужно настроить исключение и добавить некоторые переменные-члены, чтобы автоматически дополнять и печатать необходимую информацию при печати стека исключений.

Проследите код, выводящий стек исключений:

...
    public void printStackTrace() {
        printStackTrace(System.err);
    }
...
    public void printStackTrace(PrintStream s) {
        printStackTrace(new WrappedPrintStream(s));
    }
...
    private void printStackTrace(PrintStreamOrWriter s) {
        // Guard against malicious overrides of Throwable.equals by
        // using a Set with identity equality semantics.
        Set<Throwable> dejaVu =
            Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());
        dejaVu.add(this);

        synchronized (s.lock()) {
            // Print our stack trace
            s.println(this);
            StackTraceElement[] trace = getOurStackTrace();
            for (StackTraceElement traceElement : trace)
                s.println("\tat " + traceElement);

            // Print suppressed exceptions, if any
            for (Throwable se : getSuppressed())
                se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);

            // Print cause, if any
            Throwable ourCause = getCause();
            if (ourCause != null)
                ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu);
        }
    }
...

Проблема синхронизации меня пока не волнует, видно, что код для вывода имени исключения и подробной информации такой:

            s.println(this);

JVM реализует полиморфные вызовы этой ссылки во время выполнения посредством динамического связывания. Если вы продолжите трассировку, в конце концов будет вызван метод toString() этого экземпляра. Самый низкий общий класс-предок всех исключений — это класс Throwable, который предоставляет реализацию toString() по умолчанию. Большинство распространенных классов исключений не переопределяют эту реализацию. Наши пользовательские исключения также могут напрямую наследовать эту реализацию:

...
    public String toString() {
        String s = getClass().getName();
        String message = getLocalizedMessage();
        return (message != null) ? (s + ": " + message) : s;
    }
...
    public String getLocalizedMessage() {
        return getMessage();
    }
...
    public String getMessage() {
        return detailMessage;
    }
...

Очевидно, формат печати по умолчанию — это формат информации об исключении из примера: имя исключения (полное имя) + подробная информация. DetailMessage устанавливается пользователем при создании исключения, поэтому, если есть пользовательская переменная-член, мы обычно вставляем эту переменную в метод toString(). Ссылаться наcom.sun.javaws.exceptionsв упаковкеBadFieldExceptionи посмотрите, как он вставляет поле и значение пользовательских переменных-членов:

  public String toString() {
    return this.getValue().equals("https")?"BadFieldException[ " + this.getRealMessage() + "]":"BadFieldException[ " + this.getField() + "," + this.getValue() + "]";
  }

Строго говоря,BadFieldExceptionПеременная-член поля не вставляется непосредственно в toString. Однако это не влияет на наше понимание, и заинтересованные читатели могут самостоятельно прочитать исходный код.

Суммировать

Согласно информации об исключении, отладка является базовым навыком для программиста.Здесь мы сделали предварительное исследование процесса чтения и печати информации об исключении.Мы также разберемся с часто используемыми классами исключений в будущем.Несколько основных правил, которые должны помнить программисты, чтобы лучше понять, как использовать исключения для написания чистого кода.

Довольно совершенный механизм обработки исключений в Java — это палка о двух концах: при правильном использовании он может улучшить читабельность и надежность кода, при плохом — сделать код более неуправляемым. Например, вызов метода-члена для нулевого указателя вызовет исключение во время выполнения, что естественно, однако неконтролируемо ждать, пока он вызовет исключение в определенной точке и месте (на самом деле «определено»). , но является «неопределенным» для отладки), или можно контролировать проверку и активную выдачу исключений в начале метода ввода? Кроме того, какие исключения следует обрабатывать немедленно, а какие следует продолжать выбрасывать на внешний уровень? Когда вы бросаете на внешний слой, когда вам нужно обернуть исключение? Взгляните на String#toLowerCase(), см. ProcessBuilder#start() и почувствуйте это.


Ссылка на эту статью:Вы действительно читаете сообщения об исключениях Java?
автор:обезьяна 007
Источник:monkeysayhi.github.io
Эта статья основана наCreative Commons Attribution-ShareAlike 4.0Международное лицензионное соглашение выпущено, его можно перепечатать, вывести или использовать в коммерческих целях, но авторство и ссылка на эту статью должны быть сохранены.