Exception和Error
基本区别
特性 | Exception |
Error |
---|---|---|
含义 | 程序可以预期并处理的异常情况 | 通常不可恢复的严重问题 |
用途 | 业务逻辑错误、输入校验失败、网络超时等 | 程序 bug、内存溢出、类型错误等 |
处理方式 | 通常会用 try-catch 捕获并恢复 |
很少捕获,通常要修复代码本身 |
继承关系 | 继承自 Exception |
继承自 Error (也实现了 Exception 接口) |
示例 | FormatException , IOException |
TypeError , OutOfMemoryError |
Exception —— 可预期异常
- 是 可控的,发生后程序依然能继续运行。
- 通常用于表示业务错误或外部环境问题。
void parseAge(String input) {
var age = int.parse(input);
if (age < 0) {
throw Exception("年龄不能为负数");
}
}
void main() {
try {
parseAge("-5");
} catch (e) {
print("捕获到异常: $e");
}
}
Error —— 不可恢复的错误
- 通常代表代码逻辑或系统级错误。
- 即使捕获了,也很难安全恢复。
void causeError() {
var list = [1, 2, 3];
print(list[5]); // RangeError
}
void main() {
try {
causeError();
} catch (e, s) {
print("捕获到错误: $e");
print("堆栈信息: $s");
}
}
统一处理
在 Dart 中 Error
其实也实现了 Exception
接口,所以 catch (e)
可以同时捕获 Exception
和 Error
。
如果你想区分:
try {
// 可能出错的代码
} on Exception catch (e) {
print("这是 Exception: $e");
} on Error catch (e) {
print("这是 Error: $e");
}
Dart 的异常处理机制
Dart 通过 抛出(throw) 和 捕获(try-catch-finally) 来处理异常。
Dart 中所有对象(不仅仅是 Exception
或 Error
)都可以作为异常抛出,不过推荐抛 Exception
或其子类。
Dart 没有 Java 那样的 checked exception(受检异常),所以函数声明时不用写 throws。
抛出异常
一般使用
void validateAge(int age) {
if (age < 0) {
throw Exception('年龄不能为负数');
}
}
void main() {
validateAge(-5); // 会抛出异常
}
在 Dart 里 throw
后面可以是任何对象,不一定非得是 Exception
或 Error
。不过这么做在实际项目里很少见,因为会降低代码可读性。
抛出字符串
void main() {
try {
throw '这是一个字符串异常';
} catch (e) {
print('捕获到异常: $e'); // 捕获到异常: 这是一个字符串异常
print('类型: ${e.runtimeType}'); //类型: String
}
}
抛出自定义对象
class MyProblem {
final String message;
MyProblem(this.message);
@override
String toString() => 'MyProblem: $message';
}
void main() {
try {
throw MyProblem('配置文件缺失');
} catch (e) {
print('捕获: $e'); // 捕获: MyProblem: 配置文件缺失
print('类型: ${e.runtimeType}'); // 类型: MyProblem
}
}
抛出数字
void main() {
try {
throw 404;
} catch (e) {
print('捕获到异常: $e'); //捕获到异常: 404
print('类型: ${e.runtimeType}'); //类型: int
}
}
自动向上抛出异常
Dart 的异常传播是沿调用栈从下向上冒泡的过程。
Dart 异常是 向调用栈上抛 的,不会像 Java 那样要求声明 throws
。
只有遇到第一个匹配的 try-catch
才会停止冒泡。
如果最外层(main
)也没捕获,程序会直接终止并输出未捕获异常。
调用开始:
┌───────────────────────────────┐
│ main() │ <-- 最外层
│ 调用 method1() │
│ 调用 method2() │
└───────────────────────────────┘
执行到 method2():
┌───────────────────────────────┐
│ main() │
│ 调用 method1() │
│ 调用 method2() │
│ throw FormatException() │ <-- 异常发生
└───────────────────────────────┘
异常冒泡过程:
1. method2() 没有 try-catch → 异常抛给 method1()
2. method1() 没有 try-catch → 异常抛给 main()
3. main() 有 try-catch → 捕获并处理
异常传播方向(从下往上):
method2() ❌ → method1() ❌ → main() ✅
最终结果:
main 捕获到异常: FormatException: 格式错误
捕获异常
try-catch
void main() {
try {
int.parse('abc'); // FormatException
} catch (e) {
print('捕获异常: $e');
}
}
- 没有
on
,会捕获所有异常(包括Error
和Exception
)。 - 如果需要更精细的类型处理,就要手动判断类型。
try-on
try {
throw OutOfMemoryError(); // 模拟内存不足错误");
} on OutOfMemoryError {
print('捕获到异常: $e'); // 捕获到异常: 2.718281828459045
print('没有内存了');
// rethrow;
} catch (e) {
print('捕获到异常: $e'); // 捕获到异常: OutOfMemoryError
print(e);
}
on Type
只做 类型匹配,不接收异常对象。如果你不需要访问异常对象(比如只打印固定的提示),这种写法最简洁。
不能直接在里面用
e
,因为没声明变量。
try-on-catch
(指定类型)
void main() {
try {
int.parse('abc');
} on FormatException catch (e) {
print('格式错误: $e');
} on Exception catch (e) {
print('其他异常: $e');
}
}
on Type catch (e)
也是按类型匹配,但同时会接收异常对象到e
里。- 如果需要使用异常信息、堆栈等,必须这样写。
三个的对比
写法 | 匹配方式 | 能否访问异常对象 | 场景 |
---|---|---|---|
on Type { ... } |
按类型 | ❌ | 只关心类型,不关心异常内容 |
on Type catch (e) |
按类型 | ✅ | 关心类型且需要异常信息 |
catch (e) |
所有异常 | ✅ | 最通用的捕获 |
获取堆栈信息
void main() {
try {
int.parse('abc');
} catch (e, stack) {
print('异常: $e');
print('堆栈: $stack');
}
}
finally 语句块
finally
无论是否发生异常都会执行,适合做资源清理:
void main() {
try {
int.parse('abc');
} catch (e) {
print('捕获异常: $e');
} finally {
print('不管异常与否,这里都会执行');
}
}
rethrow(重新抛出)
有时你捕获异常后,想在当前层做部分处理,然后继续让上层处理,可以用 rethrow
:
void main() {
try {
handle();
} catch (e) {
print('main 捕获: $e');
}
}
void handle() {
try {
int.parse('abc');
} catch (e) {
print('handle 捕获并记录日志');
rethrow; // 继续抛给上层
}
}
异步异常处理
async
/await
中的异常用普通的 try-catch
捕获:
Future<void> loadData() async {
try {
await Future.delayed(Duration(seconds: 1));
throw Exception('网络错误');
} catch (e) {
print('捕获到: $e');
}
}
如果用 then
/catchError
:
Future.delayed(Duration(seconds: 1))
.then((_) => throw Exception('网络错误'))
.catchError((e) => print('捕获: $e'));
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1909773034@qq.com