【Flutter】Dart:异常

在软件开发中,程序可能会遇到意外的错误或异常情况,导致无法继续正常执行。这种情况称为异常。为了增强程序的鲁棒性,Dart 提供了强大的异常处理机制。通过处理异常,我们可以在程序运行过程中捕获并应对各种错误,防止应用程序崩溃。本文将详细介绍 Dart 中的异常,包括异常的抛出、异常的捕获以及如何创建自定义异常。

什么是异常

异常 (Exception)是在程序运行时发生的一种错误情况,它可能是由于逻辑错误、资源不可用或数据处理问题等原因引起的。Dart 提供了内置的异常类来表示不同类型的错误,例如 ExceptionError,以及如何通过 try-catch 等语句进行异常处理。

在 Dart 中,异常是通过抛出(throw)来引发的,程序员可以选择捕获(catch)并处理这些异常,或者将它们向上传递。

Dart 中的异常类型

Dart 中的异常类型大致分为两类:

  • Exception :表示程序执行过程中遇到的预期问题。开发者可以捕获并处理这些异常。例如,FormatException 表示数据格式不正确。

  • Error :表示程序中严重的、通常无法恢复的问题。Error 是 Dart 底层系统错误,比如 OutOfMemoryError。一般来说,程序员不应该捕获 Error,而是应该让其向上传递,最终由 Dart 虚拟机处理。

常见的异常类包括:

  • Exception:通用异常类型。
  • FormatException:格式错误异常,通常用于字符串格式转换出错的场景。
  • ArgumentError:参数错误异常,表示传递的参数无效。
  • RangeError:范围错误异常,通常用于索引超出范围的情况。
  • StateError:状态错误异常,表示操作在当前对象状态下无效。
  • UnsupportedError:表示操作不受支持。

抛出异常

Dart 使用 throw 关键字显式地抛出异常。任何对象都可以作为异常抛出,但通常会抛出 Exception 或其子类的实例。

dart 复制代码
void checkAge(int age) {
  if (age < 18) {
    throw Exception('Age must be at least 18.');
  }
  print('You are eligible to vote.');
}

void main() {
  checkAge(16);  // 这将抛出异常
}

在这个例子中,当 age 小于 18 时,Exception 将被抛出,错误消息为 'Age must be at least 18.'

抛出自定义消息的异常

Dart 允许开发者抛出自定义的异常消息,便于更好地描述错误:

dart 复制代码
void divide(int a, int b) {
  if (b == 0) {
    throw Exception('Division by zero is not allowed.');
  }
  print(a / b);
}

void main() {
  divide(10, 0);  // 这将抛出异常 'Division by zero is not allowed.'
}

捕获异常

捕获异常可以防止程序崩溃并允许开发者根据异常类型做出响应。在 Dart 中,通过 try-catch 语句捕获异常。

使用 try-catch 捕获异常

try-catch 语句用于捕获并处理抛出的异常。try 块中的代码如果抛出异常,程序将跳转到 catch 块进行处理。

语法:

dart 复制代码
try {
  // 可能会抛出异常的代码
} catch (e) {
  // 捕获并处理异常
}

示例:

dart 复制代码
void main() {
  try {
    int result = 10 ~/ 0;  // 整数除法,抛出异常
    print(result);
  } catch (e) {
    print('Caught an exception: $e');  // 捕获并处理异常
  }
}

在这个示例中,10 ~/ 0 会抛出除以零的异常,被 catch 块捕获并输出相应的错误信息。

捕获特定类型的异常

catch 块可以捕获所有异常,但如果需要处理特定类型的异常,可以在 catch 中指定异常的类型。

dart 复制代码
void main() {
  try {
    int result = int.parse('abc');  // 这将抛出 FormatException
    print(result);
  } on FormatException catch (e) {
    print('Caught a FormatException: $e');
  }
}

在这个例子中,只有 FormatException 会被捕获并处理。如果抛出其他类型的异常,它将不会被这个 catch 块捕获。

捕获堆栈跟踪信息

有时,了解异常发生时的堆栈跟踪信息对于调试至关重要。Dart 提供了第二个参数来捕获堆栈跟踪信息:

dart 复制代码
void main() {
  try {
    int result = 10 ~/ 0;
    print(result);
  } catch (e, s) {
    print('Caught an exception: $e');
    print('Stack trace: $s');  // 输出堆栈跟踪
  }
}

在这个示例中,s 包含了异常发生时的堆栈跟踪信息,方便调试错误。

异常处理的 finally

finally 块用于定义无论是否抛出异常都要执行的代码。它通常用于关闭资源或释放占用的资源,例如文件或网络连接。

语法:

dart 复制代码
try {
  // 可能抛出异常的代码
} catch (e) {
  // 捕获异常
} finally {
  // 始终执行的代码
}

示例:

dart 复制代码
void main() {
  try {
    int result = 10 ~/ 0;
    print(result);
  } catch (e) {
    print('Caught an exception: $e');
  } finally {
    print('This code always executes.');
  }
}

无论是否捕获到异常,finally 块中的代码都会执行。在上面的例子中,即使捕获到了除零异常,finally 块中的 'This code always executes.' 也会被输出。

自定义异常

在 Dart 中,我们可以创建自定义异常类,以便在特定情况下抛出特定类型的异常。这可以帮助我们更精确地描述错误并处理它们。

定义自定义异常类

自定义异常类可以通过扩展 Exception 类来实现。以下是自定义异常的示例:

dart 复制代码
class AgeException implements Exception {
  String errorMessage() {
    return 'Age must be greater than 18';
  }
}

void checkAge(int age) {
  if (age < 18) {
    throw AgeException();
  }
  print('Age is valid');
}

void main() {
  try {
    checkAge(16);
  } catch (e) {
    print(e.errorMessage());  // 输出自定义的错误信息
  }
}

在这个例子中,AgeException 是一个自定义异常类,当 age 小于 18 时抛出异常。我们可以通过调用 errorMessage() 方法来获取自定义的错误信息。

自定义异常与内置异常的不同

自定义异常提供了更好的可读性和维护性。与内置异常不同,自定义异常可以根据业务需求提供特定的错误消息和行为。例如,当我们想要处理特定领域的错误(如用户输入错误、业务逻辑错误等)时,自定义异常可以使代码更加简洁明了。

常见异常处理最佳实践

只捕获可以处理的异常

捕获所有异常可能会掩盖真正的错误。建议只捕获你能够处理的异常类型。滥用 catch 所有异常的写法会导致一些意料之外的错误难以排查。

使用 on 捕获特定异常

如果知道某个代码段可能抛出的具体异常类型,使用 on 来捕获特定异常比使用 catch 更加高效且明确。

保证资源释放

在进行文件操作、数据库访问或网络请求时,务必使用 finally 确保资源释放,例如关闭文件流或断开连接。即使发生异常,finally 也会执行,防止资源泄露。

提供有意义的错误信息

当抛出异常时,提供清晰且有意义的错误消息有助于调试和定位问题。自定义异常时,尤其要注意这一点。

相关推荐
1024小神11 分钟前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛19 分钟前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee
TheITSea22 分钟前
云服务器宝塔安装静态网页 WordPress、VuePress流程记录
java·服务器·数据库
AuroraI'ncoding29 分钟前
SpringMVC接收请求参数
java
AiFlutter1 小时前
Flutter通过 Coap发送组播
flutter
九圣残炎1 小时前
【从零开始的LeetCode-算法】3354. 使数组元素等于零
java·算法·leetcode
Y多了个想法1 小时前
RK3568 android11 适配敦泰触摸屏 FocalTech-ft5526
android·rk3568·触摸屏·tp·敦泰·focaltech·ft5526
天天扭码1 小时前
五天SpringCloud计划——DAY1之mybatis-plus的使用
java·spring cloud·mybatis
程序猿小柒2 小时前
leetcode hot100【LeetCode 4.寻找两个正序数组的中位数】java实现
java·算法·leetcode
NotesChapter2 小时前
Android吸顶效果,并有着ViewPager左右切换
android