说明:对于一个
java程序员来讲,这太简单了!学习过程非常无聊,所以对教程做了很多摘录!
摘录:根据条件的满足情况,来执行不同的逻辑代码块,这就是
条件流程的作用。Dart编程中,主要有两种语法形式:if条件语句和switch条件语句,两者适用于不同的场景。在现实生活中,
if...else...的语境是如果...就...否则...就...,而switch是当...时,就...。
if 和 bool 型变量形影不离,它会根据条件的真假,来执行不同的代码块。所以一个 if...else 语句可以表示两段分支的抉择,另外如果 else 代码块中没有处理内容,可以省略。
闲聊*:这些东西,基本所有的编程语言都是极为相似的!
import 'dart:math';
main() {
bool condition = Random().nextBool();
if (condition) {
// condition = true 时执行逻辑
} else {
// condition = false 时执行逻辑
}
}
else ifimport 'dart:math';
main() {
// 获取随机数
int random = Random().nextInt(100);
// 多分支判断
if (random < 50) {
print('小于50');
} else if (random < 80) {
print('小于80');
} else {
print('大于等于80');
}
}
if 通过对 bool 型变量进行检验,来选择执行的分支。 switch 是针对一般对象进行的校验,按分支执行,可以说 switch 是更高级的分支结构。它可以很轻松地实现多分支的结构,而不必像 if..else 那样冗长的判断。
import 'dart:math';
main() {
// 获取随机数
int random = Random().nextInt(3);
// switch 语句
switch (random) {
case 0:
print('0');
break;
case 1:
print('1');
break;
case 2:
print('2');
break;
default:
print('default');
}
}
switch 校验对象的两个限制case 后面匹配值,必须是 常量;== 方法(顺带说一句,像 String 、int 这样的类中,确实覆写了 == 操作符,但在 dart 层并未实现,这是允许的。)。一般编程中的主要循环是
for循环和while循环。
基本的编程语言都有 for 循环,这里不做过多阐述。
摘录教程:标准的
for循环结构如下,for关键字后的括号中有三个部分,且通过;隔开。其中startExp是进入循环体前执行的表达式,只会执行一次;condition是bool型的变量,相当于循环的阀门,只有为true时,才允许执行循环体。eachLoopExp是在每次循环体结束之后触发的表达式,一般用于修改condition的条件。
for ( startExp ; condition ; eachLoopExp ) {
loopBody
}
main() {
// 一个字符串列表
List<String> list = ['apples', 'bananas', 'oranges'];
// 使用 for 循环遍历列表
for (int i = 0; i < list.length; i++) {
print(list[i]);
}
}
apples
bananas
oranges
摘录:
for...in只是对列表遍历的一个语法糖,它可以屏蔽循环三大件,直接遍历访问元素。这样有益有弊,好处是使用方便,坏处是无法直接感知遍历到的索引位置。
main() {
// 一个字符串列表
List<String> list = ['apples', 'bananas', 'oranges'];
// 使用 for...in 循环遍历列表
for (String item in list) {
print(item);
}
}
apples
bananas
oranges
while 循环的结构比较简单,但它要比 for 循环更难理解一点。while 关键字后的括号中填入校验条件,条件满足会进入循环体。从这可以看出,必须在 condition 或 loopBody 中动态修改条件,否则就会死循环。
while (condition) {
loopBody
}
while 循环与 for 循环的等价改写
for循环和while循环本质上没有什么区别,两者可以进行相互转化。其实对于循环而言,最重要的是对校验条件的维,如下是通过while循环对上面for循环的等价改写。
List<String> cnNumUnits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
int i = 0;
bool condition = i < cnNumUnits.length;
while (condition){
print(cnNumUnits[i]);
i++;
condition = i < cnNumUnits.length;
}
main() {
// 一个字符串列表
List<String> list = ['apples', 'bananas', 'oranges'];
// 使用 while 循环遍历列表
int index = 0;
while (index < list.length) {
print(list[index]);
index++;
}
}
apples
bananas
oranges
do...while 作为循环的一种,和前两者也没有什么本质性的区别,都是依靠校验条件来不断执行循环体。它有一个最大的特点,就是:loopBody 至少执行一次,而 for 和 while 可能会由于条件不满足而一次都不执行。
do {
loopBody
} while(condition)
main() {
// 一个字符串列表
List<String> list = ['apples', 'bananas', 'oranges'];
// 使用 do while 循环遍历列表
int i = 0;
do {
print(list[i]);
i++;
} while (i < list.length);
}
apples
bananas
oranges
摘录:对于循环来说有个非常重要的事:如何中断循环。正常来说,循环的终止是靠修改校验条件实现的。除此之外,我们还有其他的手段在
循环体内中断或终止流程。常用的关键字有:continue break return
- 1
- 2
- 3
continue 表示该 次 循环体到此执行完毕,进入下一次循环。比如下面案例中,当 i 是偶数时,tag1 触发 continue ,比如当 i = 2 时,该次循环到此结束,也就是下一步不会到达 tag2 。进入下一次循环,是指触发 eachLoopExp 表达式,这里是 i++ ,由于循环条件仍然满足,所以会继续进入循环,此时 `i = 3
int sum = 0;
for (int i = 0; i < 10; i++) {
if(i.isEven){// 如果是偶数
continue; //tag1
}
sum += i; //tag2
print("第 $i 次循环,计入 sum");
}
print("sum: $sum");
break 相对而言就比较 "强硬" 一些,只要某次循环中走到 break , 就会完全中断循环,不会进入下次循环。把上面代码的 tag1 处改为 break ,也就是说只要 i 是偶数,整个循环就会被完全终止。
int sum = 0;
for (int i = 0; i < 10; i++) {
if(i.isEven){// 如果是偶数
break; //tag1
}
sum += i; //tag2
print("第 $i 次循环,计入 sum");
}
print(sum); // tag3
严格来说,
return关键字并非用于循环的流程控制,而是表示当前方法已经结束。由于方法结束,其中的循环自然也就不会再进行了。比如下面tag1处如果是return,那么该方法直接退出。连tag3都不会执行。
int sum = 0;
for (int i = 0; i < 10; i++) {
if(i.isEven){// 如果是偶数
return; //tag1
}
sum += i; //tag2
print("第 $i 次循环,计入 sum");
}
print("sum: $sum"); // tag3
摘录:在程序运行的过程中,可能会出现一些可以预见的错误:比如,网络异常、文件读取异常、日期格式化异常、数值越界异常等。一旦发生异常,不作处理的话,整个程序就会因错误而停止,无法执行之后的任务。另外异常的信息记录也能让开发者更容易定位问题,所以对异常的捕获和处理是非常重要的。
如下测试案例中 task1 方法将入参字符串转换为数字,如果传入非数字,就会产生转换异常。从而中断程序,导致 task2 无法触发。
void main(){
task1('a');
task2();
}
int task1(String num){
return int.parse(num);
}
void task2(){
print("Task2");
}
通过 try...catch... 可以对异常进行捕捉。在 catch 关键字后的括号中可以回调两个参数,如下
e 表示异常对象,此处为 FormatExceptions 是 _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");
}
异常的顶层抽象类是
Exception,其中有个factory构造。也就是说Exception可以进行构造,本质上创建的运行时类型是_Exception,可传入一个对象用于表示异常信息。

如下做个简单的测试,在
getMean方法中,传入单词名称,根据映射表来获取单词示意。当没有示意时,抛出异常,通过throw关键字,后面加Exception对象即可。
void main() {
try {
getMean("about");
} catch (e,s) {
print("${e.runtimeType}: ${e.toString()}");
print("${s.runtimeType}: ${s.toString()}");
}
}
String getMean(String arg) {
Map<String, String> dict = {"card": "卡片", "but": "但是"};
String? result = dict[arg];
if (result == null) {
throw Exception("empty $arg mean in dict");
}
return result;
}
有些场景可能存在多种不同的异常,我们期望可以区别对待,我们可以对
Exception类进行拓展。如下自定义一个NoElementInDictException表示映射中没有相关元素的异常:
class NoElementInDictException implements Exception{
final String arg;
NoElementInDictException(this.arg);
String toString() => "empty $arg mean in dict";
}
另外,一个方法可能抛出多种异常,如果希望明确抓取的类型种类,可以通过
on/catch关键字创建分支。注意,允许有若干个on分支进行不同类型异常处理,对于未匹配到的异常,会走默认的catch分支。
void main() {
try {
getMean("about");
} on NoElementInDictException catch(e,s){
// 特定种类的异常处理
} catch (e,s) {
// 其余异常处理
}
}
final 关键字
finally关键字用于catch代码块之后,无论异常与否,其后代码块的逻辑总会被执行。如果不用finally关键字,那就需要在每个异常分支以及外界都写一遍finally代码块,这无疑是很麻烦的。这也是finally关键字的价值所在。
void foo2(){
try {
getMean("about");
} catch (e,s) {
print("${e.runtimeType}: ${e.toString()}");
print("${s.runtimeType}: ${s.toString()}");
} finally{
print("finally bloc call");
}