
3.7 异常
在代码实际运行中,难免会出现一些问题导致程序发生错误甚至崩溃,我们称之为异常。在Dart中,也会抛出(即出现)异常和捕捉异常。如果异常未经补货,就很可能造成程序崩溃。
Dart编程语言提供了Exception和Error两类异常。除此之外,还有很多它们的子类,甚至还可以自定义异常类型,其中自定义的异常类型不能为空值。下面来看一看如何抛出异常,以及如何捕捉它们。
3.7.1 Throw
先来看一下如何抛出一个已知类型的异常:

运行代码,控制台将会出现报错提示:

一旦程序发生异常,就会终止运行,后面的逻辑将不会被执行。实际上,如果尝试在IDE中抛出异常的代码后面继续写代码的话,就会出现Dead Code警告。因为IDE检测到了抛出异常的代码,在此之后的代码就不会被执行,所以在此后面的代码均为Dead Code。
那么,如何自定义异常呢?同样非常简单,代码片段如下:

运行代码,控制台依然会有报错提示:

从输出的结果上看,自定义的Custom exception已经得到体现。
当然,这样的自定义异常似乎是没有任何意义的,只能做讲解演示用。在实际生产环境中,自定义异常通常的做法是写一个类,声明Error或Exception类中的方法。这会涉及类、继承等知识,我们会在下一章讲解。
3.7.2 Catch
更多的时候,我们希望程序能够捕捉一些可能发生的异常情况,以避免程序崩溃。这就好像生活中我们打的预防针,因为身体提前有了抗体,所以就可以抵抗病毒了,而在Dart中是通过Catch来捕捉异常的。首先尝试运行如下代码:

我们知道列表的下标是从0开始的,而上例中最后一个下标是4,但在print()方法中却取了5号下标,因此,程序会崩溃。运行结果如下:

千万不要以为自己能够完全规避这类问题。当程序的逻辑愈加复杂时,开发者有可能真的会出现这样的问题。而问题一旦发生,程序就会崩溃,对于使用者就是一次非常不好的体验。
为了规避程序崩溃的风险,正确的做法实际上是将值改正确。但在这里为了讲解方便,故意不改这个错误的值,而是使用Catch来捕获这个异常并处理它。代码片段如下:

运行结果:

在上面的代码中,首先使用一个try的代码块来包裹可能出现异常的代码,然后catch紧跟其后,e是包含异常信息的对象。处理的方法是将异常信息输出,然后按照正确的写法输出值,便得到了上面的运行结果。
在某些情况下,由于被try包裹起来的代码不一定只发生一种类型的异常,所以就需要分别对每一种异常进行处理。对于这种分情况进行处理的需求,只需要去捕捉不同的异常即可。代码如下:

运行结果:

这一次并没有输出详细的错误信息,因为代码的逻辑走到了on RangeError分支,而输出详细的错误信息是作为默认异常处理才会被执行的。
在某些情况下,我们可能需要再次抛出异常,尽管这种情况很少见。其实再次抛出异常很简单,只需要在catch的代码块中使用rethrow即可。代码如下:

运行结果:


3.7.3 Finally
当在运行完一些可能引起异常的代码逻辑后,仍需要执行一些语句完成后面的工作时,就需要使用finally代码块包裹后面的代码。具体参考如下示例:

运行结果:

当然,这是没有抛出异常的情况。你可以自行尝试依然执行print(intArray[5]),观察在出现异常时的运行结果。