有时当我运行我的应用程序时,它会给我一个错误,看起来像这样:
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)
人们将其称为“堆栈跟踪”。什么是堆栈跟踪?关于程序中发生的错误,它能告诉我什么?
关于这个问题,我经常看到一个新手程序员“得到一个错误”,他们只是简单地粘贴他们的堆栈跟踪和一些随机的代码块,而不了解堆栈跟踪是什么或如何使用它。此问题旨在作为新手程序员的参考,他们可能需要帮助来理解堆栈跟踪的值。
补充一下罗伯提到的。在应用程序中设置断点可以逐步处理堆栈。这使开发人员能够使用调试器来查看方法在确切的哪个点上执行了未预料到的操作。
由于Rob已经使用NullPointerException (NPE)来说明一些常见的问题,我们可以通过以下方式帮助消除这个问题:
如果我们有一个接受参数的方法,比如:
在我们的代码中,我们希望计算firstName包含一个值,我们将这样做:if(firstName == null || firstName.equals("")) return;
以上可以防止我们使用firstName作为不安全的参数。因此,通过在处理之前进行空检查,我们可以帮助确保我们的代码能够正常运行。要展开一个利用对象的方法的例子,我们可以看这里:
If (dog == null || dog. If (dog == null || dog. If)firstName == null)返回;
以上是检查null的正确顺序,我们从基本对象开始,在本例中是dog,然后开始沿着可能性树向下走,以确保在处理之前所有内容都是有效的。如果顺序颠倒,可能会抛出一个NPE,我们的程序将崩溃。
To understand the name: A stack trace is a a list of Exceptions( or you can say a list of "Cause by"), from the most surface Exception(e.g. Service Layer Exception) to the deepest one (e.g. Database Exception). Just like the reason we call it 'stack' is because stack is First in Last out (FILO), the deepest exception was happened in the very beginning, then a chain of exception was generated a series of consequences, the surface Exception was the last one happened in time, but we see it in the first place.
关键一:这里需要理解的一个棘手而重要的事情是:最深层的原因可能不是“根本原因”,因为如果你写了一些“糟糕的代码”,它可能会导致一些比它的层更深的异常。例如,一个糟糕的sql查询可能会导致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是根本原因。
这里附上一个按时间顺序对跟踪堆栈进行排序的图表。
其他文章描述了什么是堆栈跟踪,但它仍然很难使用。
如果您获得堆栈跟踪,并希望跟踪异常的原因,那么理解它的一个良好起点是使用Eclipse中的Java堆栈跟踪控制台。如果您使用另一个IDE,可能会有类似的特性,但这个答案是关于Eclipse的。
首先,确保在Eclipse项目中可以访问所有Java源代码。
然后在Java透视图中,单击Console选项卡(通常在底部)。如果控制台视图不可见,进入菜单选项“Window -> Show view”,选择“Console”。
然后在控制台窗口中,单击下面的按钮(在右侧)
然后在下拉菜单中选择“Java Stack Trace Console”。
将堆栈跟踪粘贴到控制台。然后,它将提供到您的源代码和任何其他可用源代码的链接列表。
例如,如果我们有这样一个程序:
public class ExceptionTest {
public static void main(String[] args) {
int l = trimmedLength(null);
System.out.println("Trimmed length = " + l);
}
private static int trimmedLength(String string) {
return string.trim().length();
}
}
你会得到这样的堆栈跟踪:
最近的方法调用(以及导致异常的方法调用)将位于堆栈的顶部,即顶部行(不包括错误消息文本)。在本例中,就是trimmedLength方法。从堆栈往下看,可以追溯到过去。第二行是调用第一行的方法,等等。
如果您使用的是开源软件,您可能需要下载源代码并将其附加到您的项目中进行检查。下载源jar,在你的项目中,打开references Libraries文件夹,找到开源模块的jar(带有类文件的那个),然后右键单击,选择Properties并附加源jar。
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)
补充一下罗伯提到的。在应用程序中设置断点可以逐步处理堆栈。这使开发人员能够使用调试器来查看方法在确切的哪个点上执行了未预料到的操作。
由于Rob已经使用NullPointerException (NPE)来说明一些常见的问题,我们可以通过以下方式帮助消除这个问题:
如果我们有一个接受参数的方法,比如:
在我们的代码中,我们希望计算firstName包含一个值,我们将这样做:if(firstName == null || firstName.equals("")) return;
以上可以防止我们使用firstName作为不安全的参数。因此,通过在处理之前进行空检查,我们可以帮助确保我们的代码能够正常运行。要展开一个利用对象的方法的例子,我们可以看这里:
If (dog == null || dog. If (dog == null || dog. If)firstName == null)返回;
以上是检查null的正确顺序,我们从基本对象开始,在本例中是dog,然后开始沿着可能性树向下走,以确保在处理之前所有内容都是有效的。如果顺序颠倒,可能会抛出一个NPE,我们的程序将崩溃。