一、Dart语言错误处理的基本概念

在Dart开发中,错误处理是保证程序健壮性的关键。无论是Flutter应用还是后端服务,未处理的异常都可能导致应用崩溃或数据丢失。Dart中的错误主要分为两类:Exception(可预见的异常)和Error(通常表示程序逻辑错误)。

// 示例1:Dart中的Exception和Error
void main() {
  try {
    // 人为抛出一个Exception
    throw Exception('这是一个自定义异常');
  } catch (e) {
    print('捕获到异常: $e'); // 输出:捕获到异常: Exception: 这是一个自定义异常
  }

  try {
    // 人为抛出一个Error
    throw ArgumentError('无效参数');
  } catch (e) {
    print('捕获到错误: $e'); // 输出:捕获到错误: Invalid argument: 无效参数
  }
}

关键点

  • Exception通常用于可恢复的错误(如网络请求失败)。
  • Error表示严重问题(如空指针),通常不需要捕获。

二、try-catch-finally的实战用法

Dart使用try-catch-finally处理异常,语法与其他语言类似,但支持更灵活的捕获方式:

// 示例2:捕获特定类型的异常
void fetchData() {
  try {
    var data = {'name': 'Dart'};
    print(data['age']!); // 触发空指针异常(NoSuchMethodError)
  } on NoSuchMethodError {
    print('字段不存在!'); // 精准捕获特定错误
  } catch (e, s) {
    print('其他异常: $e');  
    print('堆栈信息: $s');  // 打印调用栈
  } finally {
    print('清理资源'); // 无论是否异常都会执行
  }
}

注意事项

  • 使用on关键字可针对异常类型分别处理。
  • catch (e, s)中的s是堆栈跟踪(Stack Trace),便于调试。

三、异步代码的错误处理

Dart的异步操作(如FutureStream)需要通过特殊方式处理错误:

// 示例3:Future的错误处理
Future<void> loadUserData() async {
  try {
    var data = await Future.error('模拟网络错误');
    print(data);
  } catch (e) {
    print('异步错误: $e'); // 输出:异步错误: 模拟网络错误
  }
}

// 示例4:Stream的错误监听
Stream<int> countNumbers() async* {
  for (int i = 1; i <= 3; i++) {
    if (i == 2) throw '出错啦!';
    yield i;
  }
}

void main() {
  countNumbers().listen(
    (data) => print('数据: $data'),
    onError: (e) => print('Stream错误: $e'), // 输出:Stream错误: 出错啦!
    cancelOnError: false, // 错误后是否继续监听
  );
}

关联技术

  • async/await简化了异步代码的编写,但错误处理仍需try-catch
  • Stream的错误可通过listenonError回调处理。

四、自定义异常与最佳实践

通过继承Exception类实现自定义异常:

// 示例5:自定义业务异常
class AuthException implements Exception {
  final String message;
  AuthException(this.message);

  @override
  String toString() => 'AuthException: $message';
}

void login(String password) {
  if (password.isEmpty) {
    throw AuthException('密码不能为空');
  }
}

void main() {
  try {
    login('');
  } on AuthException catch (e) {
    print(e); // 输出:AuthException: 密码不能为空
  }
}

最佳实践

  1. 早抛出,晚捕获:在底层抛出异常,在高层统一处理。
  2. 避免捕获所有错误:只处理能恢复的异常,Error通常不应捕获。
  3. 日志记录:结合logger包记录异常上下文。

五、应用场景与技术优缺点

典型场景

  • API请求失败(如超时、404)。
  • 用户输入验证(如格式错误)。
  • 文件读写权限问题。

优缺点分析
| 优点 | 缺点 |
|------|------|
| 语法简洁,与Java/JavaScript类似 | 异步错误处理稍显复杂 |
| 支持异常类型细分 | 部分框架(如Flutter)需要额外处理Zone错误 |

总结
Dart的错误处理机制既灵活又强大,合理使用能显著提升代码可靠性。在Flutter开发中,还需注意Widget树的错误边界(通过ErrorWidget处理),但那是另一个话题了。