中断流程
循环的执行是通过循环条件来控制的,但是,有时我们想要通过额外的条件判断,来决定是否中断执行,或者中断本次循环而继续执行下次及之后的循环。
这就引出了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: 25
break结束整个循环
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
加总的次数为:7
return当前函数
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