1.3 什么是调试
像计算机科学家一样思考Java
程序中隐藏的未被发现的错误叫做漏洞(bug),跟踪和修改bug的过程叫做调试(debugging)。程序中存在三种类型的错误,将它们区分开来有助于更快地定位跟踪错误。
1.3.1 语法错误
编译器只能在程序语法正确的情况下才能完成编译任务,否则,编译将失败,程序自然也就不能运行。语法(syntax)是程序的结构和关于该结构的一些规则。
比如,在英语中,一个句子必须以大写的首字母开始,以句点结束。
对于多数读者,少量的语法错误并不是什么大的问题,但编译器就没有这么宽容了。哪怕是很小的语法错误,编译器都会报错并且退出编译过程,你也别想运行程序了。
更糟糕的是,Java中的语法规则比英语还多,而编译器给出的错误信息也不那么有用。在你编程生涯的前几个星期里,你可能会把大量的时间花在语法错误上。随着经验的增加,你的语法错误将会减少,查找错误也会变得更快。
1.3.2 运行时错误
第二种错误类型是运行时错误。之所以叫“运行时错误”,是因为这些错误直到程序运行时才会出现。在Java中,解释器在执行字节码的过程中发生的错误称为运行时错误。
Java是一种趋向于安全的语言,这意味编译器会捕获大量的错误。因此运行时错误并不多见,特别是简单程序。
在Java中,运行时错误称为异常(exception)。在多数情况下,当异常发生时,通常会有窗口或对话框弹出以显示此时程序正在进行的操作。这些信息对于调试来说是很有用的。
1.3.3 逻辑错误和语义错误
第三种错误称为逻辑错误(logicerror)或语义错误(semanticerror)。当程序中存在这样的错误时,编译过程虽然能够完成,也不会生成错误信息,但程序却不能完成正确的工作。具体来说,程序是按照你所写的去执行的,但问题在于你所写的程序并没有表达出你真实的意图。也可以说,此时的程序从语义上讲是错误的。对这种逻辑错误的识别是比较困难的,因为我们必须向后看,根据程序的输出来查找错误。
1.3.4 实验性调试
在本课程中,调试是你将要学习掌握的最重要的技能之一。虽然调试有时是件令人沮丧的事情,但同时也是程序开发中最有趣、最有挑战性和最有价值的活动之一。
调试就像侦探工作一样,你根据一堆线索来推断导致结果的原因。
调试同时也像一门实验性科学。一旦你意识到有什么地方出了错误,你便会修改程序再重新运行。如果你的假设成立,那么你就可以对修改后的结果进行预测,进而离成功更近一步。否则,你不得不做出新的假设。就像Sherlock Holmes所说:“当你排除了所有的不可能后所剩下的,不管有多么的不确定,都是事实。”(出自阿瑟·柯南·道尔《四个签名》)
对于有些人来说,编程和调试是一回事情,即编程是逐步调试直到满足要求的过程。在一开始,程序应该就能运行,然后不断地进行修改,这样,在整个开发过程中,你的程序都是可以运行的。
比如,Linux操作系统包含成千上万行代码,然而它起初只是Linus Torvalds为了研究Intel 80386芯片而开发的一个小程序。Linus一开始只是想实现将输出的“AAAA”字符串变为“BBBB”,后来,就这么一个小程序演化成了Linux操作系统。
在后续章节中,我将针对调试和其他编程实践提出更多的建议。