什么是堆栈跟踪,如何使用它来调试应用程序错误?

允我心安 提交于 2020-01-07 01:45:55

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

有时,当我运行我的应用程序时,它给我一个错误,看起来像:

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)

人们将其称为“堆栈跟踪”。 什么是堆栈跟踪? 关于程序中发生的错误,它能告诉我什么?


关于这个问题-我经常看到一个问题,这个问题是新手程序员“遇到错误”的地方,他们只是粘贴了堆栈跟踪和一些随机的代码块,而不了解堆栈跟踪是什么或如何使用它。 该问题旨在为可能需要帮助来了解堆栈跟踪值的新手程序员提供参考。


#1楼

Throwable系列提供了另一个堆栈跟踪功能-可以操作堆栈跟踪信息。

标准行为:

package test.stack.trace;

public class SomeClass {

    public void methodA() {
        methodB();
    }

    public void methodB() {
        methodC();
    }

    public void methodC() {
        throw new RuntimeException();
    }

    public static void main(String[] args) {
        new SomeClass().methodA();
    }
}

堆栈跟踪:

Exception in thread "main" java.lang.RuntimeException
    at test.stack.trace.SomeClass.methodC(SomeClass.java:18)
    at test.stack.trace.SomeClass.methodB(SomeClass.java:13)
    at test.stack.trace.SomeClass.methodA(SomeClass.java:9)
    at test.stack.trace.SomeClass.main(SomeClass.java:27)

操纵堆栈跟踪:

package test.stack.trace;

public class SomeClass {

    ...

    public void methodC() {
        RuntimeException e = new RuntimeException();
        e.setStackTrace(new StackTraceElement[]{
                new StackTraceElement("OtherClass", "methodX", "String.java", 99),
                new StackTraceElement("OtherClass", "methodY", "String.java", 55)
        });
        throw e;
    }

    public static void main(String[] args) {
        new SomeClass().methodA();
    }
}

堆栈跟踪:

Exception in thread "main" java.lang.RuntimeException
    at OtherClass.methodX(String.java:99)
    at OtherClass.methodY(String.java:55)

#2楼

其他文章描述了堆栈跟踪是什么,但是仍然很难使用。

如果您获得了堆栈跟踪并希望跟踪异常原因,那么理解它的一个很好的起点就是使用Eclipse中Java Stack Trace Console 。 如果您使用其他IDE,可能会有类似的功能,但是此答案与Eclipse有关。

首先,请确保您可以在Eclipse项目中访问所有Java源代码。

然后在Java透视图中,单击“ 控制台”选项卡(通常在底部)。 如果“控制台”视图不可见,请转到菜单选项“ 窗口”->“显示视图”,然后选择“ 控制台”

然后在控制台窗口中,单击以下按钮(在右侧)

然后从下拉列表中选择Java Stack Trace Console

将堆栈跟踪信息粘贴到控制台中。 然后,它将提供指向您的源代码和任何其他可用源代码的链接的列表。

这可能是您所看到的(来自Eclipse文档的图像):

由最近的方法调用将是堆栈的顶部 ,这是顶线(不包括消息文本)。 往下走可以追溯到过去。 第二行是调用第一行的方法,依此类推。

如果使用开源软件,则可能需要下载源并将其附加到项目中。 下载源jar,在您的项目中,打开Referenced Libraries文件夹以找到您的开源模块(带有类文件的jar),然后右键单击,选择“ 属性”并附加源jar。


#3楼

我发布此答案,因此最高的答案(按活动排序时)不是一个完全错误的答案。

什么是Stacktrace?

stacktrace是一个非常有用的调试工具。 它显示了引发未捕获的异常时(或手动生成stacktrace时)的调用堆栈(即,到那时为止已调用的函数堆栈)。 这非常有用,因为它不仅可以向您显示错误发生的位置,而且还可以告诉您程序在代码的那个位置是如何结束的。 这导致下一个问题:

什么是例外?

运行时环境用来告诉您发生错误的是异常。 流行的示例是NullPointerException,IndexOutOfBoundsException或ArithmeticException。 当您尝试做一些不可能的事情时,都会导致这些。 例如,当您尝试取消引用Null对象时,将抛出NullPointerException:

Object a = null;
a.toString();                 //this line throws a NullPointerException

Object[] b = new Object[5];
System.out.println(b[10]);    //this line throws an IndexOutOfBoundsException,
                              //because b is only 5 elements long
int ia = 5;
int ib = 0;
ia = ia/ib;                   //this line throws an  ArithmeticException with the 
                              //message "/ by 0", because you are trying to
                              //divide by 0, which is not possible.

我应该如何处理Stacktraces / Exceptions?

首先,找出导致异常的原因。 尝试在Google上搜索该异常的名称,以查明该异常的原因是什么。 在大多数情况下,这是由错误的代码引起的。 在上面给出的示例中,所有异常都是由错误的代码引起的。 因此,对于NullPointerException示例,您可以确保此时a永远不会为null。 例如,您可以初始化a或包括如下检查:

if (a!=null) {
    a.toString();
}

这样,如果a==null则不会执行违规行。 其他示例也是如此。

有时,您无法确定自己没有例外。 例如,如果您在程序中使用网络连接,则无法阻止计算机失去其Internet连接(例如,您不能阻止用户断开计算机的网络连接)。 在这种情况下,网络库可能会引发异常。 现在,您应该捕获异常并进行处理 。 这意味着,在具有网络连接的示例中,您应尝试重新打开连接或通知用户或类似的信息。 另外,每当您使用catch时,始终仅捕获要捕获的异常, 请勿使用像catch (Exception e)这样的广泛的catch语句来捕获所有异常。 这非常重要,因为否则您可能会意外捕获错误的异常并以错误的方式做出反应。

try {
    Socket x = new Socket("1.1.1.1", 6789);
    x.getInputStream().read()
} catch (IOException e) {
    System.err.println("Connection could not be established, please try again later!")
}

为什么我不应该使用catch (Exception e)

让我们用一个小例子来说明为什么您不应该只捕获所有异常:

int mult(Integer a,Integer b) {
    try {
        int result = a/b
        return result;
    } catch (Exception e) {
        System.err.println("Error: Division by zero!");
        return 0;
    }
}

该代码尝试执行的操作是捕获由0除以可能引起的ArithmeticException但是,如果abnull ,则它还会捕获可能抛出的NullPointerException 。 这意味着,您可能会收到NullPointerException但将其视为ArithmeticException并可能做错了事。 在最好的情况下,您仍然会错过NullPointerException。 这样的东西会使调试变得更加困难,所以不要那样做。

TLDR

  1. 找出导致异常的原因并加以解决,以使它根本不会引发异常。
  2. 如果不可能1.捕获特定的异常并进行处理。

    • 永远不要只添加try / catch,然后忽略异常! 不要那样做!
    • 从不使用catch (Exception e) ,始终捕获特定的Exceptions。 这样可以省去很多麻烦。

#4楼

仅添加到其他示例中,就有带有$符号出现的inner(nested)类 。 例如:

public class Test {

    private static void privateMethod() {
        throw new RuntimeException();
    }

    public static void main(String[] args) throws Exception {
        Runnable runnable = new Runnable() {
            @Override public void run() {
                privateMethod();
            }
        };
        runnable.run();
    }
}

将导致此堆栈跟踪:

Exception in thread "main" java.lang.RuntimeException
        at Test.privateMethod(Test.java:4)
        at Test.access$000(Test.java:1)
        at Test$1.run(Test.java:10)
        at Test.main(Test.java:13)

#5楼

要了解名称,请执行以下操作 :堆栈跟踪是一个异常列表(或者您可以说“原因”列表),从最表面的异常(例如,服务层异常)到最深层的异常(例如,数据库异常)。 就像我们称之为“堆栈”的原因一样,因为堆栈是先进先出(FILO),最深层的异常发生在一开始,然后异常链产生了一系列后果,表面异常是最后一个事情及时发生了,但我们首先看到了它。

关键1 :这里需要理解的一个棘手和重要的事情是:究其根本原因可能不是“根本原因”,因为如果您编写一些“不良代码”,则可能在其底层深层引发某些异常。 例如,错误的sql查询可能会导致在bottem中重置SQLServerException连接而不是syndax错误,该错误可能恰好在堆栈的中间。

-> 在中间找到根本原因是您的工作。

关键2 :另一个棘手但重要的事情是在每个“ Cause by”块内部,第一行是最深的层,并且在该块中居第一位。 例如,

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)

Book.java:16被Auther.java:25调用,而Bootstrap.java:14则被调用,Book.java:16是根本原因。 在此附上一个图表,按时间顺序对跟踪堆栈进行排序。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!