Java 调试
此页面由 Jeanne Boyarsky 在 UPL 下贡献什么是调试?
我们都编写完美无缺的代码,第一次尝试就能运行,对吧?哈哈!开个玩笑。我们经常需要在代码中查找和修复错误。这个过程称为调试。
你可能想知道为什么它被称为“调试”。这个词在 1940 年代流行起来,当时海军上将 Grace Hopper 在一台计算机中发现了一只飞蛾(请记住,当时的计算机非常庞大,所以一只真正的昆虫可以进入。)虽然你不需要处理代码中的动物,但我们必须处理错误和问题,我们称之为 bug。你可能也听到过它们被称为缺陷,尽管这通常是在你提交代码之后。无论问题叫什么名字,你仍然需要找到那个问题,这就是调试!
调试器是 IDE(集成开发环境)中的一个工具,它允许你查看程序中不同点不同变量的值。它就像一个功能强大的放大镜。虽然 Eclipse/IntelliJ/NetBeans/VS Code 之间有一些细节不同,但概念是一样的。
当你运行代码时,你可以选择以常规/运行模式或调试模式启动它。这允许你决定何时进行调试。
为什么不使用 Println?
在第一次学习如何编码时,我们经常编写这样的代码来查看发生了什么
System.out.println(numCats);
使用println
没有错(只要你不提交它)。但是,在更复杂的程序中,它很快就会变得难以管理,尤其是在你需要跟踪多个内容或有很多循环的情况下。当应用程序中有大量日志时,也很难找到它。(我使用星号和我的首字母来缓解这种情况,但仍然可以超过println
有用的程度。)
System.out.println("*** JB i=%s numCats=%s numDogs=%s".formatted(i, numCats, numDogs));
即使println()
目前满足你的需求,它也不会永远满足。学习如何使用调试器有助于避免你仅仅因为只知道它而使用 println 的问题。
为什么不使用单元测试?
单元测试是编写代码来测试一小段代码,只需点击一下按钮。单元测试没有错。单元测试很棒。它们记录了预期行为。它们会告诉你是否返回了意外的值。它们帮助你理解代码的行为。有时它们甚至可以给你关于代码问题的大线索。但是,它们不会告诉你当代码没有返回正确值时,它内部发生了什么。为此,你需要使用单元测试的调试器来查看方法内部发生了什么。一旦你修复了代码,单元测试可以帮助你避免引入新的错误!
@Test
void magic() {
assertEquals(42, target.magic(), "magic number incorrect");
}
什么是断点?
我(不正确地)实现了 magic 方法。它应该将六乘以七得到 42。但是,事实并非如此。经过(不是)太多调查后,我感到困惑,意识到我想知道第 5 行part1
和part2
的值是什么。
这就是断点的作用。它允许我告诉调试器在该处暂停程序,并让我四处查看。你可能会问,我如何设置断点?在代码附近的左侧栏中,你可以双击(或右键单击并选择打开断点)。将出现一个小圆圈,显示我设置的断点。
public class Answer {
public int magic() {
int part1 = 3 + 2; // BUG!
int part2 = 7;
return part1 * part2;
}
}
重要的是以调试模式运行程序,以便断点生效。(请记住,如果你没有以调试模式运行,所有断点都会被忽略。)当调试器在第 5 行停止时,我看到part2
是我预期的七。但是part1
是五,而不是六。我找到了错误!感谢调试器允许我设置断点。
如何使用调试器?
你可能需要使用调试器的原因有很多。最常见的三个原因是
- 修复损坏的代码 - 调试器允许你查看代码运行时变量的值。这允许你查看代码在何处停止按预期行为运行。
- 理解不熟悉的代码 - 观察代码运行时每个变量的值可以帮助你更好地理解它。
- 跟踪代码路径 - 当在断点处停止时,调试器会显示为了到达那里而调用的类/方法。你甚至可以点击它们来查看这些点处的变量范围。
调试器基础
有四个基本的调试器命令来控制调试器在第一个断点处停止后的执行流程。对于每个命令,我们将使用Flow
类作为示例。
public class Flow {
public static void main(String args[]) {
System.out.println(debugging());
}
public static int debugging() {
int num = investigate();
num++;
return num;
}
public static int investigate() {
int found = 5;
return found;
}
}
- 单步执行 - 告诉程序执行,但只执行到方法调用的第一行。假设我在第 7 行有一个断点。当我告诉调试器“单步执行”时,它会转到第 13 行。
- 步过 - 告诉程序执行,但不要在任何方法中停止。如果我在第 7 行有一个断点,并告诉调试器“步过”,调试器将转到第 8 行。再次选择“步过”将使调试器转到第 9 行。
- 单步退出/返回 - 告诉程序运行到方法的末尾,并返回到调用者。如果我在第 13 行有一个断点,并选择“单步退出”或“单步返回”,调试器将位于第 7 行,并带有方法调用的结果。(单步退出和单步返回是同一件事。不同的 IDE 使用不同的名称。)
- 继续 - 告诉程序继续运行,直到遇到另一个断点或完成。
高级技巧
调试器有许多高级技巧。三个常见的技巧是
- 条件断点 - 通常,调试器会在你要求断点的地方停止。如果你在循环中,或者知道什么值会触发问题,你就不希望这样。条件断点允许你在断点中添加一些 Java 代码,以便它只在条件为真时停止。这种方法避免了需要多次点击继续,直到你到达你关心的值。
- 评估 - 一旦你到达断点,你就可以编写 Java 代码来确定事态的状况。例如,你可以在可用变量上调用方法。
- 更改数据 - 你可以在调试器中手动更改变量的值,并让代码继续运行。它将使用你新的更新值,而不是原始值。这使你能够探索潜在修复的影响。
文档
现在你已经了解了使用调试器的概念,是时候查看 IDE 的文档了!注意键盘快捷键和每个按钮的位置。
上次更新: 2023 年 11 月 5 日
返回教程列表