异常处理
Dart代码可以抛出(throw)和捕获(catch)异常(Exception)。异常代表了程序的某种错误,也就是一些意想不到的事情。引发异常的代码分支及其上下文,即测试中的负向路径。
Dart的函数或方法不会声明它将抛出哪些异常; Dart也不强制要求捕获异常。如果异常发生了却不被处理,抛出异常的 isolate 将被挂起,且通常会被终止。第7章将详细讨论 isolate,这里将其视为一种内存独立的线程(如 main 函数所在的 main isolate)即可。
抛出异常 (throw)
Dart程序可以抛出(throw)什么?答案是除了null外的任何东西。
// ex281.dart
void main() {
throw 'something bad happened';
// Unhandled exception:
// something bad happened
}
此例使用throw 关键字,抛出了一个字符串。
Exception 和 Error
Dart提供了 Exception 和 Error 类及相关子类(第4章将介绍类),官方建议我们在产品代码中抛出 Exception 或 Error 的实现类。例如下面这些类实现了Exception:
AnalysisExceptionDeferredLoadExceptionFileSystemExceptionFormatExceptionIOExceptionIsolateSpawnExceptionPathExceptionRemoteExceptionTimeoutException
我们也可以创建自己的异常类,并实现(implements) Exception 或 Error。
// ex282.dart
void main() {
throw FooException();
}
class FooException implements Exception {}
Exception 与 Error 的区别:
-
Exception旨在向外传递一个失败信息(例如超时异常TimeoutException),以便通过编程的方式解决;它应该包含有用的信息,且希望被捕获。 -
Error代表了程序员引起的错误(例如断言错误AssertionError), 它理应被避免,且通常暗示了一个bug,我们不应捕获它。
捕获异常
捕获指定类型的异常
使用 try...on...catch 语法捕获异常。将可能抛出异常的代码放在try{}代码块中,然后使用on关键字捕获特定类型的异常。
// ex283.dart
import 'dart:io';
void main() {
var str = 'dart';
try {
int.parse(str);
} on FormatException {
stderr.writeln('FormatException: Cannot parse "$str" as an integer.');
}
}
此例试图将字符串"dart"转换为一个整数,引发了 FormatException,随后被捕获。
捕获异常信息
使用catch关键字捕获异常信息(即异常类型的一个实例)。
// ex284.dart
void main() {
try {
int.parse('dart');
} on FormatException catch (ex) {
print(ex.message); // Output: Invalid radix-10 number
}
}
catch() 还有另一个可选参数,表示异常堆栈。
// ex285.dart
void main() {
try {
int.parse('dart');
} on FormatException catch (e, s) {
print(e);
print(s);
}
}
重新抛出异常
使用 rethrow关键字重新抛出异常,且保留原来的堆栈信息。
// ex286.dart
void main() {
try {
parseInt('dart');
} catch (_, s) {// 2
print(s); // 2a
}
}
int parseInt(String str) {
try {
return int.parse(str);
} on FormatException catch (_, s) {
print(s); // 1a
rethrow; // 1
}
}
- 使用
rethow关键字将捕获到的FormatException再次抛出; - 这里仅用了
catch(没有使用on指定异常类型),表示捕获所有类型的异常。
捕获多种类型的异常
使用多个catch子句来分别处理不同类型的异常。
//ex267.dart
void main() {
print(safeDivide('8', '2'));
print(safeDivide('8', '0'));
print(safeDivide('hello', 'dart'));
}
(String? err, int) safeDivide(String a, String b) {
try {
return (null, int.parse(a) ~/ int.parse(b));
} on FormatException catch (e) {
return ('FormatException: ${e.source}', 0);
} on UnsupportedError catch (e) { // IntegerDivisionByZeroException
return ('UnsupportedError: ${e.message}', 0);
} on Exception catch (e) {
return ('unknown exception: $e', 0);
}
}
finally
无论try 代码块内是否发生了异常, finally子句都将被执行。finally子句通常用来关闭资源(如打开的系统文件)。
// ex288.dart
void main() {
try {
print(2 ~/ 0);
} on UnsupportedError {
print('oops');
} finally {
print('done');
}
// Output:
// oops
// done
}