Looking for java Keywords? Try Ask4Keywords

Java Language Создание и чтение стоп-кадров


пример

Когда создается объект исключения (т. Throwable Когда вы его new ), конструктор Throwable захватывает информацию о контексте, в котором было создано исключение. Позже эта информация может выводиться в виде stacktrace, которая может использоваться для диагностики проблемы, вызвавшей исключение в первую очередь.

Печать стоп-кадра

Печать stacktrace - это просто вызов метода printStackTrace printStackTrace() . Например:

try {
    int a = 0;
    int b = 0;
    int c = a / b;
} catch (ArithmeticException ex) {
    // This prints the stacktrace to standard output
    ex.printStackTrace();
}

Метод printStackTrace() без аргументов будет печатать на стандартный вывод приложения; т.е. текущий System.out . Существуют также printStackTrace(PrintStream) и printStackTrace(PrintStream) printStackTrace(PrintWriter) перегружают эту печать в указанный Stream или Writer .

Заметки:

  1. В стеке нет сведений о самом исключении. Вы можете использовать метод toString() для получения этих деталей; например

       // Print exception and stacktrace
       System.out.println(ex);
       ex.printStackTrace();
    
  2. Печать Stacktrace следует использовать экономно; см. Pitfall - чрезмерные или неуместные стеки . Часто лучше использовать фреймворк протоколирования и передавать объект исключения для регистрации.

Понимание stacktrace

Рассмотрим следующую простую программу, состоящую из двух классов в двух файлах. (Мы показали имена файлов и добавили номера строк для иллюстрации.)

File: "Main.java"
1   public class Main {
2       public static void main(String[] args) {
3           new Test().foo();
4       }
5   }

File: "Test.java"
1   class Test {
2       public void foo() {
3           bar();
4       }
5   
6       public int bar() {
7           int a = 1;
8           int b = 0;
9           return a / b;
10      }

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

Exception in thread "main" java.lang.ArithmeticException: / by zero
        at Test.bar(Test.java:9)
        at Test.foo(Test.java:3)
        at Main.main(Main.java:3)

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

Строка №1 говорит нам, что поток, называемый «main», завершился из-за неперехваченного исключения. Полное имя исключения - java.lang.ArithmeticException , а сообщение об ошибке - «/ нолем».

Если мы рассмотрим javadocs для этого исключения, в нем говорится:

Брошено, когда произошло исключительное арифметическое условие. Например, целое число «делить на ноль» выдает экземпляр этого класса.

В самом деле, сообщение «/ by zero» является сильным намеком на то, что причиной исключения является то, что какой-то код попытался что-то делить на ноль. Но что?

Остальные 3 строки - это трассировка стека. Каждая строка представляет вызов метода (или конструктора) в стеке вызовов, и каждый из них сообщает нам три вещи:

  • имя выполняемого класса и метода,
  • имя файла исходного кода,
  • номер строки исходного кода выполняемого оператора

Эти строки stacktrace перечислены с фреймом для текущего вызова вверху. Верхний кадр в нашем примере выше находится в методе Test.bar и в строке 9 файла Test.java. Это следующая строка:

    return a / b;

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

Если нам нужно идти дальше, мы можем видеть из stacktrace, что bar() вызывается из foo() в строке 3 Test.java и что foo() в свою очередь, вызывался из Main.main() .

Примечание. Названия классов и методов в кадрах стека являются внутренними именами для классов и методов. Вам нужно будет признать следующие необычные случаи:

  • Вложенный или внутренний класс будет выглядеть как «OuterClass $ InnerClass».
  • Анонимный внутренний класс будет выглядеть как «OuterClass $ 1», «OuterClass $ 2» и т. Д.
  • Когда выполняется код в конструкторе, инициализатор поля экземпляра или блок инициализатора экземпляра, имя метода будет «".
  • Когда выполняется код в инициализаторе статического поля или статическом инициализаторе, имя метода будет «".

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

Цепочка исключений и вложенные стеки

Java SE 1.4

Цепочка исключений происходит, когда кусок кода ловит исключение, а затем создает и генерирует новый, передавая первое исключение в качестве причины. Вот пример:

File: Test,java
1   public class Test {
2      int foo() {
3           return 0 / 0;
4      }
5
6       public Test() {
7           try {
8               foo();
9           } catch (ArithmeticException ex) {
10              throw new RuntimeException("A bad thing happened", ex);
11          }
12      }
13
14      public static void main(String[] args) {
15          new Test();
16      }
17  }

Когда вышеуказанный класс скомпилирован и запущен, мы получаем следующую стек:

Exception in thread "main" java.lang.RuntimeException: A bad thing happened
        at Test.<init>(Test.java:10)
        at Test.main(Test.java:15)
Caused by: java.lang.ArithmeticException: / by zero
        at Test.foo(Test.java:3)
        at Test.<init>(Test.java:8)
        ... 1 more

Стек strace начинается с имени класса, метода и стека вызовов для исключения, которое (в данном случае) вызвало сбой приложения. За ним следует строка «Caused by:», которая сообщает об исключении cause . Сообщается имя класса и сообщение, за которым следуют стеки кадров исключения. Трассировка заканчивается символом «... N больше», который указывает, что последние N кадров такие же, как и для предыдущего исключения.

«Причиненный:» включается только в вывод, если cause первичного исключения не равна null ). Исключения могут быть цепочки неограниченно, и в этом случае stacktrace может иметь несколько следов «Caused by:».

Примечание: механизм cause был обнаружен только в Throwable API в Java 1.4.0. До этого цепочка исключений должна была быть реализована приложением с использованием настраиваемого поля исключения для представления причины и пользовательского метода printStackTrace .

Захват stacktrace как строки

Иногда приложение должно иметь возможность захватывать стек в качестве String Java, чтобы его можно было использовать для других целей. Общий подход для этого заключается в создании временного OutputStream или Writer который записывает в буфер в памяти и передает его в printStackTrace(...) .

Библиотеки Apache Commons и Guava предоставляют утилиты для захвата stacktrace как строки:

org.apache.commons.lang.exception.ExceptionUtils.getStackTrace(Throwable)

com.google.common.base.Throwables.getStackTraceAsString(Throwable)

Если вы не можете использовать сторонние библиотеки в базе кода, то выполните следующий метод:

   /**
     * Returns the string representation of the stack trace.
     *
     * @param throwable the throwable
     * @return the string.
     */
    public static String stackTraceToString(Throwable throwable) {
        StringWriter stringWriter = new StringWriter();
        throwable.printStackTrace(new PrintWriter(stringWriter));
        return stringWriter.toString();
    }

Обратите внимание, что если вы намерены проанализировать stacktrace, проще использовать getStackTrace() и getCause() чем пытаться проанализировать stacktrace.