中断流程
循环的执行是通过循环条件来控制的,但是,有时我们想要通过额外的条件判断,来决定是否中断执行,或者中断本次循环而继续执行下次及之后的循环。
这就引出了continue、break、return关键字。
 
 return 不仅用于循环的终止,其主要用于函数的返回,或者,整个代码执行的返回(不执行后续代码)。 
 
continue跳转执行下一次的循环
continue表示中止本次循环,继续之后的循环处理。
下面的示例,当i为偶数时,则不执行后面的代码,而是结束当前循环,跳到下一次的循环处理。
 
 i.isEven 判断是否是偶数 
 
int sum = 0;
for (int i = 0; i < 10; i++) {
  if(i.isEven){// 如果是偶数
    continue; // 直接跳转到下一次循环
  }
  sum += i; 
  print("第 $i 次循环,计入 sum");
}
print("sum: $sum");上面用于在偶数时不执行后面的循环体,而是跳到下一次循环,最终输出如下结果:
第 1 次循环,计入 sum
第 3 次循环,计入 sum
第 5 次循环,计入 sum
第 7 次循环,计入 sum
第 9 次循环,计入 sum
sum: 25break结束整个循环
break用来结束整个循环。是在循环条件之外结束循环的方式。
比如,在一个While(true)循环中,当条件满足时执行break结束循环,否则会一直死循环下去。
var sum = 1;
var count = 0;
while (true) {
  if (sum > 100) {
    break;
  }
  sum += sum;
  count++;
}
print("sum是:$sum");
print("加总的次数为:$count");输出结果:
sum是:128
加总的次数为:7return当前函数
return主要用于返回当前函数,表示之后的代码均不执行,而是返回(通常会返回数据)。如果用在循环中,起到结束循环、整个代码返回作用。
比如,还是上一次的循环,将终止条件改为return,后续的代码将不会执行,产生Dead code。
var sum = 1;
var count = 0;
while (true) {
  if (sum > 100) {
    return;
  }
  sum += sum;
  count++;
}
print("sum是:$sum");
print("加总的次数为:$count");
异常流程
异常是相对于正常处理流程中出现的意外,通常是可预见的,比如网络异常、文件读取异常、日期格式化异常、数值越界异常、数据值异常等。
发生异常,如果不处理,整个执行流程(或程序)就有可能因错误而停止或崩溃,无法执行之后的任务。
异常捕获就是对可能出现的错误进行捕捉,然后处理或记录异常情况,保证程序后续的执行。同时,异常信息的记录,可以更容易的定位问题的发生原因。
异常流程通常用try...catch...捕获异常,来处理对应的错误。
Dart的所有异常都是非必检异常,除了自定义异常,Dart中可以将任何非null对象作为异常抛出(通常不推荐)。
异常的捕获
如下,在task1方法中,将参数转换为int类型,但是由于传入的字符串不能转换为数字,将会发生转换异常。
void main(){
  task1('a');
  task2();
}
int task1(String num){
  return int.parse(num);
}
void task2(){
  print("Task2");
}执行时会产生未捕获或未处理的异常:
Uncaught Error: FormatException: a使用try...catch...捕获异常,catch可以获取异常回调的两个参数。
- e 表示异常对象,此处为 FormatException
- s 是 _StackTrace对象,用于记录异常的栈信息
如下,捕获异常并输出异常类型和异常的堆栈信息:
void main(){
  try{
    task1('a');
  }catch(e,s){
    print("${e.runtimeType}: ${e.toString()}"); 
    print("${s.runtimeType}: ${s.toString()}"); 
  }
  task2(); // Task2
}
int task1(String num){
  return int.parse(num);
}
void task2(){
  print("Task2");
}结果如下。通过 e 可以看出异常的信息,通过 s 可以看出发生错误时方法栈的情况,便于快速定位到异常发生的地点。
FormatException: FormatException: a
_StackTrace: FormatException: a
    at Object.wrapException (<anonymous>:344:17)
    at Object.int_parse (<anonymous>:2438:15)
    at main (<anonymous>:2595:11)
    at <anonymous>:3217:7
    at <anonymous>:3200:7
    at dartProgram (<anonymous>:3211:5)
    at <anonymous>:3219:3
    at replaceJavaScript (https://www.dartpad.dev/scripts/frame.js:19:19)
    at messageHandler (https://www.dartpad.dev/scripts/frame.js:80:13)
Task2由于捕获了错误,之后task2();继续执行。
抛出异常
与异常捕获同样重要的是“抛出异常”。
在编写很多方法或类库时,如果遇到不符合条件的情况,我们需要抛出异常,即停止程序的执行,或者交由外部代码来处理异常的情况。
异常抛出,通常都是遇到代码无法执行的情况时,以异常的形式告知停止代码的执行。需要处理修正异常情况后,才能真正的执行。
如下,在函数GetMyDate中,如果有问题,抛出throw FormatException异常:
void main(){
  GetMyDate('2022-10-10');
  GetMyDate('2022-10');
}
List<String> GetMyDate(String myDate){
  var result=myDate.split('-');
  if(result.length!=3){
    throw FormatException('格式不正确,日期字符串必须为`yyyy-(M)M-(d)d`');
  }
  return result;
}dart中除数为0不会报错
如下,除以0时:
var a=0;
var b=10;
var c=b/a;
print(c); // Infinity和js中一样,除数为0时不会报错,其结果为Infinity。
自定义异常与捕获特定类型的异常
异常的顶层抽象类是 Exception ,通过其 factory 构造可以创建Exception 对象,本质上创建的运行时类型是 _Exception ,可传入一个对象用于表示异常信息。

以上面的除数为0作为示例,我们在除法计算中,判断如果除数为0,则抛出一个异常:
double DivideMethod(int a,int b){
  if(b==0){
    throw Exception('除数不能为0');
  }
  return a/b;
}以上的抛出异常都是使用的Dart自带异常对象,有时我们需要自定义不同的异常,比如自定义一个除数异常DivideException。
double DivideMethod(int a,int b){
  if(b==0){
    throw DivideException(b);
  }
  return a/b;
}
class DivideException implements Exception{
  final int arg;
  DivideException(this.arg);
  @override
  String toString() => "除数不能为$arg";
}测试,并捕获特定的DivideException
try{
  DivideMethod(10, 0);
}
on DivideException catch(e,s){
  print("${e.runtimeType}: ${e.toString()}");
  print("${s.runtimeType}: ${s.toString()}");
} catch (e,s) {
  // 其余异常处理
}输出:
DivideException: 除数不能为0
_StackTrace: #0      DivideMethod (file:///E:/privateboolnote/CMLearningCode/Flutter/test/test.dart:30:5)
#1      main (file:///E:/privateboolnote/CMLearningCode/Flutter/test/test.dart:11:5)
#2      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#3      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)on TypeException catch用于捕获特定类型TypeException的异常。
仅捕获异常,而不获取异常参数。
try{
  DivideMethod(10, 0);
}
on DivideException {
  print("发生了异常");
} catch (e) {
  // 其余异常处理
}rethrow将捕获的异常再次抛出。
 
finally语句
无论是否抛出异常,finally语句始终执行,通常finally放在catch语句之后。用于处理一些必须的清理操作,不管是否有异常,均会被执行。
try{
  DivideMethod(10, 0);
} catch (e) {
  // 异常处理
}
finally{
  print('执行必需的清理操作');
}如果没有指定 catch 语句来捕获异常,则异常会在执行完 finally 语句后抛出:
try{
  DivideMethod(10, 0);
} finally{
  print('执行必需的清理操作');
}关于Error类型
在Dart中,除了抛出Exception类型外,还可以抛出Error类型(及其子类型),其更多的表示程序运行的失败,这些失败应该被避免。
比如最常见的参数不为空的错误:
throw ArgumentError.notNull('参数不为空的Error');输出:
Unhandled exception:
Invalid argument(s) (参数不为空的Error): Must not be null
#0      test (file:///E:/privateboolnote/CMLearningCode/Flutter/test/test2.dart:27:5)
#1      main (file:///E:/privateboolnote/CMLearningCode/Flutter/test/test2.dart:21:3)
#2      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:297:19)
#3      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)或,其他的参数错误,打印出错误相关的值:
throw ArgumentError.value(i,'参数不能小于0');Unhandled exception:
Invalid argument (参数不能小于0): -1 
                            ![[Flutter]足够入门的Dart语言系列之常见运算符或操作符](https://ucc.alicdn.com/pic/developer-ecology/b7dfb840415941dc9b7e12ce086059a7.png?x-oss-process=image/format,webp/resize,h_160,m_lfit) 
                ![[Flutter]足够入门的Dart语言系列之面向对象:类成员的可见性、常量和工厂构造函数详解](https://ucc.alicdn.com/pic/developer-ecology/9c6936bb8057480485ed00e27470286c.png?x-oss-process=image/format,webp/resize,h_160,m_lfit) 
                ![[Flutter]足够入门的Dart语言系列之函数:函数定义、调用、5种参数类型和main函数](https://ucc.alicdn.com/pic/developer-ecology/12cdbdc80928469c8d48b2de97aff9d7.png?x-oss-process=image/format,webp/resize,h_160,m_lfit)