系统化掌握 Dart 编程之异常处理(一):筑基之旅

422 阅读4分钟

image.png

前言

异常Exception)是指程序执行过程中发生的意外情况,可能导致程序崩溃无法正常工作Dart 提供了强大的异常处理机制,帮助开发者优雅地捕获和处理这些异常,确保程序的稳定性可靠性。为了系统化地掌握 Dart 的异常处理,我们将从理论基础具体实现实践应用最佳实践四个层面进行详细讲解。

千曲而后晓声,观千剑而后识器。虐它千百遍方能通晓其真意

一、理论基础

理论基础 —— 理解异常的本质分类

1.1、定义

异常是程序执行过程中出现的错误意外情况。当程序遇到异常时,如果不加以处理,可能会导致程序终止或产生不正确的结果。Dart 使用对象来表示异常,并通过 throw 关键字抛出异常,使用 trycatchfinally 语句来捕获和处理异常。

1.2、特点

  • 1、可控性:可以通过代码控制异常的发生和处理。
  • 2、灵活性:可以自定义异常类型,满足不同场景的需求。
  • 3、可读性清晰的异常处理逻辑使代码更易于理解和维护。

1.3、分类

Dart 中的异常主要分为两类:

  • Error
    • 表示严重错误,通常是程序中的致命问题,如内存不足堆栈溢出等。
    • 这些错误通常不可恢复程序应立即终止
  • Exception
    • 表示可恢复的异常,如用户输入错误文件未找到等。
    • 可以通过异常处理机制捕获并处理这些异常,使程序继续运行。

二、具体实现

具体实现 —— 掌握异常处理的关键语法

2.1、抛出异常

可以使用 throw 关键字抛出一个异常对象。这个对象可以是任何类型的对象,但通常是一个继承自 ExceptionError 的类实例。

void checkAge(int age) {
  if (age < 0) {
    throw Exception('年龄不能为负数');
  }
}

void main() {
  try {
    checkAge(-5);
  } catch (e) {
    print(e); // 输出: Exception: 年龄不能为负数
  }
}

2.2、自定义异常

创建自己的异常类,继承自 Exception 类,以便更好地描述特定类型的错误。

class NegativeAgeException implements Exception {
  final String message;

  NegativeAgeException(this.message);

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

void checkAge(int age) {
  if (age < 0) {
    throw NegativeAgeException('年龄不能为负数');
  }
}

void main() {
  try {
    checkAge(-5);
  } catch (e) {
    print(e); // 输出: NegativeAgeException: 年龄不能为负数
  }
}

2.3、捕获异常

使用 try-catch 结构捕获并处理异常。

void main() {
  try {
    int result = divide(10, 0);
    print('结果是: $result');
  } on NegativeAgeException catch (e) {
    print('年龄异常: $e');
  } on Exception catch (e) {
    print('一般异常: $e');
  } catch (e) {
    print('未知异常: $e');
  } finally {
    print('清理操作...');
  }
}

int divide(int a, int b) {
  if (b == 0) {
    throw Exception('除数不能为零');
  }
  return a ~/ b;
}

三、实践应用

实践应用 —— 处理真实世界的问题

3.1、异常传播

如果一个函数抛出了异常且没有被捕获,该异常会向上层调用栈传播,直到被某个 catch 块捕获或最终导致程序终止。

void main() {
  try {
    performOperation();
  } catch (e) {
    print('主函数捕获到异常: $e');
  }
}

void performOperation() {
  riskyFunction();
}

void riskyFunction() {
  throw Exception('风险操作失败');
}

3.2、异步异常处理

异步操作(如 Future)也可能抛出异常。可以使用 awaittry-catch 来捕获这些异常。

import 'dart:async';

Future<int> fetchUserAge() async {
  await Future.delayed(Duration(seconds: 1));
  throw Exception('用户信息获取失败');
}

void main() async {
  try {
    int age = await fetchUserAge();
    print('用户年龄: $age');
  } catch (e) {
    print('发生异常: $e');
  }
}

四、最佳实践

枯燥中来点乐趣

image.png

4.1、明确异常类型

尽量使用具体的异常类型,而不是泛型的 Exception,这有助于更精确地处理不同类型的错误。

class NegativeValueException implements Exception {
  final String message;

  NegativeValueException(this.message);

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

void checkValue(int value) {
  if (value < 0) {
    throw NegativeValueException('值不能为负数');
  }
}

void main() {
  try {
    checkValue(-5);
  } on NegativeValueException catch (e) {
    print(e); // 输出: NegativeValueException: 值不能为负数
  } catch (e) {
    print('其他异常: $e');
  }
}

4.2、不要忽略异常

捕获异常后应进行适当的处理,不要简单地忽略它们

void readFile(String path) {
  try {
    // 模拟文件读取操作
    throw Exception('模拟文件读取失败');
  } catch (e) {
    // 错误处理逻辑
    print('无法读取文件: $e');
    // 记录日志或通知用户
  }
}

void main() {
  readFile('nonexistent_file.txt');
}

4.3、合理使用 finally

确保关键资源释放清理操作,即使发生异常也要保证程序的稳定性

import 'dart:io';

void writeFile(String content) {
  File file = File('example.txt');
  RandomAccessFile? raFile;

  try {
    raFile = file.openSync(mode: FileMode.write);
    raFile.writeStringSync(content);
  } catch (e) {
    print('写入文件时出错: $e');
  } finally {
    // 确保关闭文件资源
    raFile?.closeSync();
    print('文件已关闭');
  }
}

void main() {
  writeFile('Hello, World!');
}

4.4、避免过度捕获

不要在不必要的地方捕获异常,保持异常处理逻辑的简洁性可读性

void divideNumbers(int a, int b) {
  if (b == 0) {
    throw Exception('除数不能为零');
  }
  print(a ~/ b);
}

void main() {
  try {
    divideNumbers(10, 0);
  } catch (e) {
    print('捕获到异常: $e');
  }

  // 正常情况下不捕获异常
  divideNumbers(10, 2);
}

4.5、记录日志

对于生产环境中的异常,建议记录详细的日志信息,便于后续排查分析

void divideNumbers(int a, int b) {
  if (b == 0) {
    throw Exception('除数不能为零');
  }
  print(a ~/ b);
}

void main() {
  try {
    divideNumbers(10, 0);
  } catch (e) {
    print('捕获到异常: $e');
  }

  // 正常情况下不捕获异常
  divideNumbers(10, 2);
}

五、总结

枯燥中来点乐趣

image.png

通过上述四个层面 —— 理论基础具体实现实践应用最佳实践,我们系统化地掌握了 Dart 的异常处理机制。以下是各部分的核心要点:

  • 1、理论基础:理解异常的本质分类及其在 Dart 中的表现形式。
  • 2、具体实现:掌握如何抛出捕获处理异常,包括自定义异常类的创建。
  • 3、实践应用:通过实际案例展示如何应对真实世界中的异常情况,特别是异步操作中的异常处理
  • 4、最佳实践:提供一系列实用的建议,可构建更加健壮可靠的异常处理策略。

欢迎一键四连关注 + 点赞 + 收藏 + 评论