JavaRush /Java 博客 /Random-ZH /堆栈跟踪以及它与什么一起吃
Alukard
第 37 级
London

堆栈跟踪以及它与什么一起吃

已在 Random-ZH 群组中发布
在本文中,您将学习并了解 Java 现象 StackTrace(也称为调用堆栈跟踪)的工作原理。此信息是为在 Java 语法级别 9 开始时遇到此概念的初学者构建的。 我想你们所有人,至少一次,在使用 IDE 时遇到过类似的错误,无论是IdeaEclipse还是其他东西。
Exception in thread "main" java.lang.ArithmeticException
	at com.example.task01.Test.division(Test.java:10)
	at com.example.task01.Test.main(Test.java:6)
正如您可能已经猜到的,这就是我们的追踪。但不要急于惊慌,现在我们将为您分解这个例子。首先,您需要了解它的工作原理StackTrace正如Стэк其名称所示。在这一点上,我们将详细讨论一下。 Stack 集合如何工作 在第八级,您已经熟悉了集合并知道它们分为三组Set- 集合、List- 列表、Map- 字典(或映射)。根据 JavaRush (c)。 我们 Stack是该团体的一部分List。其运行原理可以描述为LIFO,即后进先出。也就是说,这是一个类似于一摞书的列表;为了取出我们Stack首先放入的元素,我们需要首先提取之后添加到列表中的所有元素。如上图所示,与常规列表不同,ArrayList我们可以通过索引从列表中获取任何元素。 再次进行强化。Стэка只能从最后获取元素!而添加到其中的第一个元素位于开头(或底部,这样更方便)。Stack 这些是我们的对象具有的 方法push()- 将元素添加到堆栈顶部。对象pop()- 返回堆栈顶部的元素,并在此过程中将其删除。对象peek()- 返回堆栈顶部的元素,但不删除它。int search()- 在堆栈上搜索元素。如果找到,则返回其距堆栈顶部的偏移量。否则返回-1。boolean empty()- 检查堆栈是否为空。如果堆栈为空则返回 true。如果堆栈包含元素,则返回 false。那么为什么Java需要StackTrace一个基于操作原理的模型呢Stack?让我们看一下下面这个简单程序执行过程中发生的错误示例。
public class Test {

    public static void main(String[] args) {
        System.out.println(convertStringToInt(null));
    }

    public static int convertStringToInt(String s) {
        int x = Integer.parseInt(s);
        return x;
    }
}
我们有一个Test有两种方法的类。大家都很熟悉mainconvertStringToInt其逻辑就是将从外部(即方法main)接收到的字符串转换为整数类型并返回int。正如您所看到的,我们故意传递参数而不是带有某个数字的字符串null。我们的方法无法正确处理该参数并导致错误NumberFormatException。如您所知,程序开始从方法中计算出其工作main,此时它创建一个新的Стэк方法,并为其命名,StackTrace并将其工作的当前值置于数字1下,然后我们再次转到该方法convertStringToInt和程序将我们位置的参数输入到之前StackTrace在编号2parseInt下创建的参数中,然后调用类中肉眼不可见的方法,这将已经是我们的Integer元素编号3StackTrace,在该方法中将添加另一个内部调用到StackTrace数字4检查元素是否为null,这将导致错误。程序需要显示我们的错误,指示错误发生之前的整个转换链。这就是之前用我们的转换数据创建的那个来帮助她的地方StackTrace
Exception in thread "main" java.lang.NumberFormatException: null
	at java.base/java.lang.Integer.parseInt(Integer.java:614)
	at java.base/java.lang.Integer.parseInt(Integer.java:770)
	at com.example.task01.Test.convertStringToInt(Solution.java:10)
	at com.example.task01.Test.main(Solution.java:6)
在错误发生之前,程序会深入研究方法,但是一旦错误发生,一切就开始以相反的顺序发生。打印描述问题的一行(示例中的 No.1),然后获取添加到我们的最后一个(顶部)值,Стэк它是第四个值并打印到控制台(示例中的 No.2),然后我们看到问题出现在Integer第 614 行代码的类中,并调用同一类方法的第 770 行parseInt(示例中的第 3 行),当添加到该方法时,Стэк该方法是第 3 行,并且该类方法,Integer对我们来说仍然不可见,它已经被我们convertStringToInt程序第 10 行的方法调用了(示例中的第 4 行,添加时是第二个),而它又在第main6 行(第 5 行)上被调用在示例中以及添加时分别为第一个)。因此,通过Стек逐步存储我们调用的方法,我们能够返回到main并行打印确切导致错误的信息。但这StackTrace不仅可以处理错误,还可以让我们获得许多有关应用程序流程的有趣信息。我们来看第9级主讲评论中另一个流行的例子。我们有了代码,我会立即附上一张图片,直观地展示程序的流程:
public class Test {
    public static void main(String[] args) {
        method1();
        method2();
    }
    public static void method1() {
        //не вызывает ничего
    }
    public static void method2() {
        method3();
        method4();
    }
    public static void method3() {
        //не вызывает ничего
    }
    public static void method4() {
        method5();
    }
    public static void method5() {
        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
        for (StackTraceElement element:stackTraceElements) {
            System.out.println(element.getMethodName());
        }
    }
}
堆栈跟踪及其与什么一起吃 - 2 到这里,我们的程序就完美地完成了它的工作并结束了。这是我们将在控制台输出中看到的内容:
getStackTrace
method5
method4
method2
main

Process finished with exit code 0
我们是如何得出这个结论的?第五种方法(从第 20 行开始)发生了什么?恐怕我能做的最好的就是将用户Kirill从评论中最流行的解释(缩写)添加到讲座中。让我们转向创建线StackTrace并逐个分析它:
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement[]- 数组类型的指示(在早期阶段,您已经了解了像 int[]、String[] 这样的数组,这里是相同的)。 stackTraceElements- 数组的名称可以是任何名称,考虑到一般命名规则,这不会影响工作。 Thread.currentThread()- 获取当前线程的链接,在该线程中执行我们想要跟踪的方法(目前这并不重要,您将在 Java Core 任务的第 16 级更详细地分析线程) getStackTrace()- 我们获得所有Стэк被调用的方法(这是 的常规 getter StackTrace)现在让我们看看创建的数组可能对我们有用。我们知道数组存储有关已执行方法的信息。(c) 为此,在第 21 行,我们启动一个修改后的循环(顺便forforEach一句,对于那些还没有研究过这个循环的人,我建议您阅读它)并将数据从数组输出到控制台,即有关在使用构造的工作期间执行了哪些方法的信息element.getMethodName()注意,正如我们所看到的,数组的零元素分别是其自身,getStackTrace()因为在接收数据数组时,它是最后一个执行的方法,因此最终位于顶部Стэка,并记住我们的构造“ “后进先出”立即第一个添加到数组中的零元素下。以下是我们还可以得到的内容StackTraceElement: String getClassName()- 返回类的名称。字符串getMethodName()- 返回方法的名称。字符串getFileName()- 返回文件名(一个文件中可以有多个类)。字符串getModuleName()- 返回模块名称(可以为空)。字符串getModuleVersion()- 返回模块版本(可以为空)。int getLineNumber()- 返回调用该方法的文件中的行号。 现在您已经了解了一般的操作原理,我建议您StackTrace在您的Ide中亲自尝试不同的方法。即使你还没有完全掌握所有内容,继续学习,谜题就会像我在这件事上遇到的那样。祝大家一切顺利!Ps 如果您喜欢本篇内容,请点赞支持。这对你来说并不困难,我很高兴。谢谢,41 级见;)
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION