异常错误处理

Dart 所有异常都是非检查异常,不用函数声明抛出类型,不强制捕获

两类:

  • Exception:业务逻辑可预期错误(格式、参数等,建议捕获处理)
  • Error:程序致命错误(类型错误、空指针、未实现等,一般不捕获)

任意非 null 对象都能 throw,规范项目只抛 Exception/Error 子类

未捕获异常会终止当前隔离区(isolate),程序崩溃

throw 抛出异常

1. 抛系统内置异常

void main() {
  // 格式异常
  throw FormatException("至少需要1段数据");
}
Code language: Dart (dart)
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Unhandled exception:
FormatException: 错误描述
#0      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:3:3)
#1      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#2      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code language: PHP (php)

以上代码,就抛出一个你自己定义的异常,你可以自己写描述,执行的时候会报错,也许你会觉得这代码有什么用?但是其实抛出异常是很重要的,例如用户注册的时候,要验证用户输入是否规范,如果不规范,就直接提示错误,这个时候,可以直接抛出异常,告诉用户出错了,这样避免不对的数据污染,保证数据规范。

2. 抛出任意对象

不推荐生产环境

void main() {
  throw "文字描述!";
}Code language: Dart (dart)

执行结果

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Unhandled exception:
文字描述!
#0      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:2:3)
#1      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#2      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code language: PHP (php)

3. 箭头函数内 throw(throw 是表达式)

class Point {}
void distanceTo(Point other) => throw UnimplementedError("该方法未实现");

void main() {
  distanceTo(Point());
}
Code language: JavaScript (javascript)

三、try /on/catch 捕获异常

语法区分

  • on 异常类型:只匹配指定类型异常,拿不到异常对象
  • catch(e):捕获所有异常,拿到异常实例 e
  • catch(e, s):e = 异常对象,s = 堆栈信息 StackTrace

1:指定单一异常捕获 on


//自定义一个异常类 继承Exception
class OutOfLlamasException implements Exception {}

//抛出上面自定义的异常
void breedMoreLlamas() {
  throw OutOfLlamasException();
}

void buyMoreLlamas() {
  print("执行了buyMoreLlamas");
}

void main() {
  try {
    breedMoreLlamas(); //调用函数 执行这里
  } on OutOfLlamasException {
    buyMoreLlamas(); //如果捕捉到OutOfLlamasException异常 就执行这里
  }
}Code language: Dart (dart)

执行结果

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
执行了buyMoreLlamasCode language: CSS (css)
try  {
    //你认为会报错的代码
} on excetion {
   //如果捕捉到错误 执行的代码
}Code language: Dart (dart)

try on 的只是一个捕捉异常的写法。

2:多分支分层捕获

精准匹配优先

class OutOfLlamasException implements Exception {}

void breedMoreLlamas() {
  throw FormatException("数据格式错误");
}

void main() {
  try {
    breedMoreLlamas();
  }
  // 1. 精准匹配自定义异常
  on OutOfLlamasException {
    print("OutOfLlamasException 出错了");
  }
  // 2. 匹配所有Exception子类
  on Exception catch (e) {
    print("普通业务异常:$e");
  }
  // 3. 兜底捕获Error、任意其他对象
  catch (e) {
    print("未知严重错误:$e");
  }
}
Code language: Dart (dart)

输出 : 普通业务异常:FormatException: 数据格式错误

可以通过多个on 来具体捕捉某个Exception。还可以使用catch 来兜底。

3:捕获异常 + 打印堆栈 StackTrace

void main() {
  try {
    int a = null;
    print(a + 1);
  } catch (e, stack) {
    print("异常信息:$e");
    print("完整堆栈:\n$stack");
  }
}
Code language: Dart (dart)

以上代码 不仅仅输出异常提示,还可以把代码调用的堆栈也输出出来

D:\dartdemo\firstdart>dart run
Building package executable...
Failed to build firstdart:firstdart:
bin/firstdart.dart:3:13: Error: A value of type 'Null' can't be assigned to a variable of type 'int'.
    int a = null;Code language: PHP (php)

四、rethrow

部分处理,继续向上抛出

作用:当前函数处理一部分逻辑,再把异常抛给上层调用者捕获

有些时候,我们不仅仅代码中处理异常,可能还会再往上(调用的地方)抛出异常,希望往上再处理一下。

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); // true不能自增,运行时报错
  } catch (e) {
    print("misbehave 局部处理:${e.runtimeType}");
    rethrow; // 重新抛出,交给外层try
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print("main 最终处理:${e.runtimeType}");
  }
}
Code language: Dart (dart)

执行过程:

misbehave 局部处理:NoSuchMethodError
main 最终处理:NoSuchMethodError

上面代码不仅仅在misbehave函数中处理,还在main函数中也处理了

五、finally 最终块

无论是否报错一定执行

两种场景:

  1. 只有 try + finally:报错不捕获,执行完 finally 后程序崩溃
  2. try + catch + finally:先处理异常,再执行 finally(资源释放专用)

1:try + finally

void cleanLlamaStalls() => print("清理资源,兜底用的(必定执行)");
void breedMoreLlamas() => throw Exception("出错了");

void main() {
  try {
    breedMoreLlamas();
  } finally {
    cleanLlamaStalls();
  }
}
Code language: Dart (dart)

执行结果,下面虽然执行了代码,但是最终都抛出了异常了。

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
清理资源,兜底用的(必定执行)
Unhandled exception:
Exception: 出错了
#0      breedMoreLlamas (file:///D:/dartdemo/firstdart/bin/firstdart.dart:2:27)
#1      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:6:5)
#2      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#3      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code language: PHP (php)

2:try + catch + finally

资源释放标准写法,例如打开文件的是,要释放

void cleanLlamaStalls() => print("关闭文件/释放资源");
void breedMoreLlamas() => throw FormatException("解析失败");

void main() {
  try {
    breedMoreLlamas();
  } catch (e) {
    print("捕获异常:$e");
  } finally {
    cleanLlamaStalls();
  }
}
Code language: Dart (dart)

执行顺序:触发异常 → catch 处理 → finally 执行

执行结果

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
捕获异常:FormatException: 解析失败
关闭文件/释放资源

六、assert 断言

仅开发调试,生产直接失效

assert(布尔条件, [可选错误提示])

条件为 false 抛出 AssertionError;生产环境完全忽略,不执行。

1:基础断言

void main() {
  String? text;
  int number = 120;
  String url = "http://foxdevelop.com";

  assert(text != null); // 校验非空
  assert(number < 100); // 数值范围校验
  assert(url.startsWith("https")); // 链接协议校验
}
Code language: JavaScript (javascript)

以上代码执行,但是什么都没有输出!

原因是你需要开启的,否则默认是不启用断言的,继续往下看

2:自定义错误信息

void main() {
  String url = "http://test.com";
  assert(
    url.startsWith("https"),
    "链接 $url 必须以 https 开头",
  );
}
Code language: JavaScript (javascript)

我们可以把断言失败的提示信息,自己来定义的。

启用断言方式

  1. Flutter Debug 模式默认开启
  2. dart 命令运行:dart --enable-asserts 你的dart文件名.dart
  3. 生产打包:断言全部移除,无性能损耗
D:\dartdemo\firstdart\bin>dart --enable-asserts firstdart.dart
Unhandled exception:
'file:///D:/dartdemo/firstdart/bin/firstdart.dart': Failed assertion: line 6 pos 10: 'text != null': is not true.
#0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:67:4)
#1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:49:5)
#2      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:6:10)
#3      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#4      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code language: PHP (php)

dart rundart 命令一样,默认关闭断言,需要追加 --enable-asserts 参数:

运行

dart run --enable-asserts main.dartCode language: CSS (css)

如果终端已经在项目目录,直接:

运行

dart run --enable-asserts

dart run:直接运行 Dart 源码,适合开发调试

dart 文件名:等价简易运行

dart compile exe:编译二进制成品,编译产出永远禁用 assert

我们回到bin目录是上一级,你的dart文件是在bin目录的,cd到bin目录的上一级, 执行

dart run –enable-asserts

如下第一个是,直接到bin目录执行 dart的文件,第二个是在上级目录 ,项目根目录执行。

D:\dartdemo\firstdart\bin>dart --enable-asserts firstdart.dart
Unhandled exception:
'file:///D:/dartdemo/firstdart/bin/firstdart.dart': Failed assertion: line 6 pos 10: 'text != null': is not true.
#0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:67:4)
#1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:49:5)
#2      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:6:10)
#3      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#4      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)

D:\dartdemo\firstdart\bin>dart run
Building package executable...
Built firstdart:firstdart.

D:\dartdemo\firstdart\bin>cd ..

D:\dartdemo\firstdart>dart run --enable-asserts
Building package executable...
Built firstdart:firstdart.
Unhandled exception:
'file:///D:/dartdemo/firstdart/bin/firstdart.dart': Failed assertion: line 6 pos 10: 'text != null': is not true.
#0      _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:67:4)
#1      _AssertionError._throwNew (dart:core-patch/errors_patch.dart:49:5)
#2      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:6:10)
#3      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#4      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code language: PHP (php)

如果断言是true的话,就不会抛出异常

上面代码改为 String url = “https://foxdevelop.com”; http改为https,text != null 改为==,number = 99

void main() {
  String? text;
  int number = 99;
  String url = "https://foxdevelop.com";

  assert(text == null); // 校验非空
  assert(number < 100); // 数值范围校验
  assert(url.startsWith("https")); // 链接协议校验
}Code language: Dart (dart)

再次执行 没有抛出异常

D:\dartdemo\firstdart>dart run --enable-asserts
Building package executable...
Built firstdart:firstdart.Code language: CSS (css)

七、例子

void cleanResource() => print("finally:释放资源");

void testError() {
  try {
    int num = 150;
    assert(num < 100, "数字必须小于100");
    throw FormatException("数据格式非法");
  } on FormatException catch (e) {
    print("捕获格式异常:$e");
    rethrow;
  } finally {
    cleanResource();
  }
}

void main() {
  try {
    testError();
  } catch (e) {
    print("main 捕获二次抛出的异常:$e");
  }
}
Code language: PHP (php)

执行结果

D:\dartdemo\firstdart>dart run --enable-asserts
Building package executable...
Built firstdart:firstdart.
finally:释放资源
main 捕获二次抛出的异常:'file:///D:/dartdemo/firstdart/bin/firstdart.dart':
 Failed assertion: line 6 pos 12: 'num < 100': 数字必须小于100Code language: PHP (php)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注