Flutter基础篇(2)-- 老司机用一篇博客带你快速熟悉Dart语法

版权声明:本文为博主原创文章,未经博主允许不得转载。https://www.jianshu.com/p/3d927a7bf020

转载请标明出处:
https://www.jianshu.com/p/3d927a7bf020
本文出自 AWeiLoveAndroid的博客


Flutter系列博文链接 ↓:

工具安装:

Flutter基础篇:

Flutter进阶篇:

Dart语法系列博文链接 ↓:

Dart语法基础篇:

Dart语法进阶篇:


【前言】Dart语言是使用flutter框架开发时候必备的语言,flutter是一个跨平台的框架,一套代码就可以完美实现安卓和ios两个平台,适配也很不错,Dart语言很友好,和java很类似,学习成本也是很低的。所以这也是我推荐学习Dart语言的一个原因。

从本篇文章开始讲解Dart语言的基本使用,我将会连续推出好几篇文章详解,希望帮助大家快速掌握Dart语言。


本文目录:

一、注释
二、关键字(56个)
三、变量和常量
四、特殊数据类型
五、运算符
六、控制流程语句
七、异常


本文代码同步发布在Github:
https://github.com/AweiLoveAndroid/Flutter-learning/tree/master/projects/dart_demo

怎么运行代码?

如果你使用IDEA或者Android Studio:

打开IDEA或者Android Studio,新建一个Flutter项目,然后在test目录运行我的代码;或者里面去写你自己的dart文件,然后右键run就可以运行了。(注意:需要连接手机或者模拟器)。

如果你使用vscode:

打开vscode,执行菜单栏运行,就可以了(确保只有一个dart文件,免得运行的文件不是你想要的,就很尴尬了,vscode也可以设置默认运行的文件是哪个,但是新手不建议去设置,很麻烦。因为你们想最快的运行效果,所有建议只有一个dart文件是最好的)。


一、注释

Dart的注释分为3种:单行注释、多行注释、文档注释。

1、单行注释以//开头。Dart编译器会忽略//和行尾之间的所有内容。

例如:// todo:待完成

2、多行注释以/*开头,以*/结尾。介于/**/两者之间的内容会被编译器忽略(除非该注释是一个文档注释)。多行注释可以嵌套。

例如:/* todo:待完成 */

3、文档注释以///或者/**开头。可以通过dartdoc命令导出文档。

文档注释的使用,例如:/// todo:待完成

文档的导出如图所示:

 

文档的导出

导出的结果在我的工程根路径的/doc/api/文件夹里面,如图所示:

导出的结果

然后浏览器打开index.html就可以看到文档了。如图所示:

本地的文档


二、关键字(60个)

5个上下文关键字(仅在特定位置具有含义。它们在任何地方都是有效的标识符)

关键字---
asynchideonshow
sync---

其中内置标志符有:(20个)

关键字---
abstractascovariantdefered
dynamicexportexternalfactory
Functiongetimplementsimport
interfacelibrarymixinoperator
partsetstatictypedef

Dart新增的,有限的保留字,支持异步功能的关键字有:(2个)

关键字---
awaityield  

33个保留字(不能使用保留字作为标识符)

关键字---
assertbreakcasecatch
classconstcontinuedefault
doelseenumextends
falsefinalfinallyfor
ifinisnew
nullrethrowreturnsuper
switchthisthrowtrue
tryvarvoidwhile
with---

跟java相比,Dart特有的关键字有:(27个)

关键字---
asasyncawaitcovariant
deferreddynamicexportexternal
factoryFunctiongethide
inislibrarymixin
onoperatorpartrethrow
setshowsynctypedef
varwithyield 

三、变量和常量

(一)变量的声明,可以使用 var、Object 或 dynamic 关键字。

创建变量并初始化变量实例:

 

var name = '张三' ;

变量存储引用。

    1. 使用Object或dynamic关键字

 

dynamic name = '张三';

调用的变量name包含对String值为“张三” 的对象的引用。
name推断变量的类型是String,但可以通过指定它来更改该类型。
如果对象不限于单一类型(没有明确的类型),请使用Object或dynamic关键字

 

Object name = '张三';
dynamic name = '李四';
    1. 显式声明将被推断的类型

比如String,int等。

 

//可以使用String显示声明字符串类型
String name = '张三' ; //代替var name = '张三';

这个类型有很多,具体在下文有介绍。

(二)默认值

未初始化的变量的初始值为null(包括数字),因此数字、字符串都可以调用各种方法。

 

//测试 数字类型的初始值是什么?
int intDefaultValue;
// assert 是语言内置的断言函数,仅在检查模式下有效
// 在开发过程中, 除非条件为真,否则会引发异常。(断言失败则程序立刻终止)
assert(intDefaultValue == null);
print(intDefaultValue);//打印结果为null,证明数字类型初始化值是null

(三)Final 和 Const的用法

如果您从未打算更改一个变量,那么使用 final 或 const,不是var,也不是一个类型。
一个 final 变量只能被设置一次;const 变量是一个编译时常量。(Const变量是隐式的final。)
final的顶级或类变量在第一次使用时被初始化。

  • 1、被final或者const修饰的变量,变量类型可以省略。

 

//可以省略String这个类型声明
final name1 = "张三";
//final String name1  = "张三";
    
const name2 = "李四";
//const String name2 = "李四";
  • 2、被 final 或 const 修饰的变量无法再去修改其值。

 

final name1 = "张三";
// 这样写,编译器提示:a final variable, can only be set once
// 一个final变量,只能被设置一次。
//name1 = "zhangsan";
    
const name2 = "李四";

// 这样写,编译器提示:Constant variables can't be assigned a value
// const常量不能赋值
// name2 = "lisi";
  • 3、注意:flnal 或者 const 不能和 var 同时使用

 

//这样写都会报错
//final var name1 = "张三";
//const var name2 = "李四";
  • 4、常量如果是类级别的,请使用 static const

 

static const speed = 100;
  • 5、常量的运算

 

const speed = 100; //速度(km/h)
const double distance = 2.5 * speed; // 距离 = 速度 * 时间

final speed2 = 100; //速度(km/h)
final double distance2 = 2.5 * speed2; // 距离 = 速度 * 时间
  • 6、const关键字不只是声明常数变量。您也可以使用它来创建常量值,以及声明创建常量值的构造函数。 任何变量都可以有一个常量值。

 

// 注意: [] 创建的是一个空的list集合
// const []创建一个空的、不可变的列表(EIL)。
var varList = const []; // varList 当前是一个EIL
final finalList = const []; // finalList一直是EIL
const constList = const []; // constList 是一个编译时常量的EIL

// 可以更改非final,非const变量的值
// 即使它曾经具有const值
varList = ["haha"];

// 不能更改final变量或const变量的值
// 这样写,编译器提示:a final variable, can only be set once
// finalList = ["haha"];
// 这样写,编译器提示:Constant variables can't be assigned a value  
// constList = ["haha"];
  • 7、只要任何插值表达式是一个计算结果为null或数字,字符串或布尔值的编译时常量,那么文字字符串就是编译时常量。(关于$表达式和不同的数据类型后面会讲解。)

 

// 这些是常量字符串
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';

// 这些不是常量字符串

var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = const [1, 2, 3];

const validConstString = '$aConstNum $aConstBool $aConstString';

//这样用就会报错:Const variables must be initialized with a constant value
// const常量必须用conat类型的值初始化。
// const invalidConstString = '$aNum $aBool $aString $aConstList';

四、特殊数据类型

Dart支持以下特殊类型:

numbers 数字
strings 字符串
booleans 布尔
lists list集合(也称为数组)
maps map集合
runes 字符(用于在字符串中表示Unicode字符)

(一)num数字类型

num是数字类型的父类,有两个子类 intdouble
num类型包括基本的运算符,如+,-,/和*,位运算符,如>>,在int类中定义。如果num和它的子类没有你要找的东西,math库可能会找到。比如你会发现abs(),ceil()和floor()等方法。

(1)int类型

int表示整数,int默认是64位二进制补码整数,int的取值不大于64位,具体取决于平台。编译为JavaScript时,整数仅限于valus,可以用双精度浮点值精确表示。可用的整数值包括-253和253之间的所有整数,以及一些幅度较大的整数。这包括一些大于2^63的整数。 因此,在编译为JavaScript的Dart VM和Dart代码之间,int类中的运算符和方法的行为有时会有所不同。例如,当编译为JavaScript时,按位运算符将其操作数截断为32位整数。
示例如下:

 

int intNum1 = 10 ;
print(intNum1);//结果是10
int intNum2 = 0xDEADBEEF ;
print(intNum2);//结果是3735928559

判断一个int值需要多少bit(位),可以使用bitLength,例如:

 

// bitLength 返回存储此int整数所需的最小位数
int a1 = 1; // 占了1个bit     相当于二进制数字 00000000 00000001
int a2 = 12; // 占了4个bit    相当于二进制数字 00000000 00001100
int a3 = 123; // 占了7个bit   相当于二进制数字 00000000 01111011
int a4 = 1234; // 占了11个bit 相当于二进制数字 00000100 11010010
print('${a1.bitLength}'); //  1
print('${a2.bitLength}');  // 4
print('${a3.bitLength}'); // 7
print('${a4.bitLength}'); // 11

(2)double类型

Dart的double是IEEE 754标准中规定的64位浮点数。double的最大值是:1.7976931348623157e+308,double类里面有一个常量maxFinite,我们通过语句print(double. maxFinite)可以得到double的最大值
如果一个数字包含一个小数,那么它就是一个double类型。示例如下:

 

double doubleNum1 = 1.1;
print(doubleNum1); //结果是1.1
double doubleNum2 = 1.42e5;
print(doubleNum2); //结果是142000.0

(3)Dart2.1里面新增特性,当double的值为int值时,int自动转成double。

例如:double test = 12;//打印结果是12.0

(4)Dart2.1,int也有api转成double。

例如:

 

int test = 10;
print(test.toDouble()); // 结果是: 10.0

(5)Dart2.1,double也有api转成int,会把小数点后面的全部去掉。

例如:

 

double test2 = 15.1;
double test3 = 15.1234;
print(test2.toInt());// 结果是15
print(test3.toInt());// 结果是15

(二)String字符串类型

Dart里面的String是一系列 UTF-16代码单元。

(1)您可以使用单引号或双引号来创建一个字符串。

 

String str1 = '单引号基本使用demo.';
String str2 = "双引号基本使用demo.";

(2)单引号或者双引号里面嵌套使用引号。

单引号里面嵌套单引号,或者//双引号里面嵌套双引号,必须在前面加反斜杠。

 

// 单引号里面有单引号,必须在前面加反斜杠
String str3 = '单引号里面有单引号it\'s,必须在前面加反斜杠.';
//双引号里面嵌套单引号(正常使用)
String str4 = "双引号里面有单引号it's.";
//单引号里面嵌套双引号(正常使用)
String str5 = '单引号里面有双引号,"hello world"';
//双引号里面嵌套双引号,必须在前面加反斜杠
String str6 = "双引号里面有双引号,\"hello world\"";

print(str3);// 双引号里面有单引号it's,必须在前面加反斜杠
print(str4);// 双引号里面有单引号it's.
print(str5);// 单引号里面有双引号,hello world"
print(str6);//双引号里面有双引号,"hello world"

(3)多个字符串相邻中间的空格问题:

除了单引号嵌套单引号或者双引号嵌套双引号不允许出现空串之外,其余的几种情况都是可以运行的。
示例如下:

 

这个会报错
//String blankStr1 = 'hello''''world';
//这两个运行正常
String blankStr2 = 'hello'' ''world'; //结果: hello world
String blankStr3 = 'hello''_''world'; //结果: hello_world


// 这个会报错
//String blankStr4 = "hello""""world";
这两个运行正常
String blankStr5 = "hello"" ""world"; //结果: hello world
String blankStr6 = "hello""_""world"; //结果: hello_world

单引号里面有双引号,混合使用运行正常

 

String blankStr7 = 'hello""""world'; //结果: hello""""world
String blankStr8 = 'hello"" ""world'; //结果: hello"" ""world
String blankStr9 = 'hello""_""world'; //结果: hello""_""world

双引号里面有单引号,混合使用运行正常

 

String blankStr10 = "hello''''world"; //结果: hello''''world
String blankStr11 = "hello'' ''world"; //结果: hello'' ''world
String blankStr12 = "hello''_''world"; //结果: hello''_''world

(4)您可以使用相邻字符串文字或+ 运算符连接字符串:

  1. 直接把相邻字符串写在一起,就可以连接字符串了。

 

  String connectionStr1 =  '字符串连接''甚至可以在''换行的时候进行。';
  print(connectionStr1);// 字符串连接甚至可以在换行的时候进行。
  1. 用+把相邻字符串连接起来。

 

  String connectionStr2 =  '字符串连接'+ '甚至可以在'+ '换行的时候进行。';
  print(connectionStr2);// 字符串连接甚至可以在换行的时候进行。
  1. 使用单引号或双引号的三引号:

 

String connectionStr3 = ''' 
  这是用单引号创建的
  多行字符串。
  ''' ;
print(connectionStr3);
String connectionStr4 = """这是用双引号创建的
  多行字符串。""";
print(connectionStr4);

print(connectionStr3)输出结果如下:

 

  这是用单引号创建的
  多行字符串。

print(connectionStr4)的输出结果如下:

 

这是用双引号创建的
  多行字符串。

(5)关于转义符号的使用

声明raw字符串(前缀为r),在字符串前加字符r,或者在\前面再加一个\
可以避免“\”的转义作用,在正则表达式里特别有用。

举例如下:

 

print(r"换行符:\n"); //这个结果是 换行符:\n
print("换行符:\\n"); //这个结果是 换行符:\n
print("换行符:\n");  //这个结果是 换行符:

(6)使用$可以获得字符串中的内容,使用${表达式}也可以将表达式的值放入字符串中。使用${表达式}时可以使用字符串拼接,也可以使用String类或者Object里面的某些方法获得相关字符串属性。

1、使用$+字符串

 

var height = 48.0;
print('当前标题的高度是$height'); //当前标题的高度是48.0

2、使用$+字符串,以及字符串拼接

 

String name = "张三";
print("$name"+"是我们的部门经理"); // 张三是我们的部门经理

3、这里使用了字符串的拼接,以及使用了String类里面的toUpperCase()函数,把字母全部变成大写。

 

String replaceStr = 'Android Studio';
assert('你知道' +
'${replaceStr.toUpperCase()}'
+ '最新版本是多少吗?' ==
'你知道ANDROID STUDIO最新版本是多少吗?'); 

注:==操作符测试两个对象是否相等。assert是断言,如果条件为true,继续进行,否则抛出异常,中端操作。

(三)bool布尔类型

Dart表示布尔值的类型叫做bool,它有两个值,分别是:truefalse,它们都是编译时常量。
Dart使用的是显式的检查值,检查值的类型,如下所示:

 

  // 检查是否为空字符串
  var emptyStr = '';
  assert(emptyStr.isEmpty);

  // 检查是否小于等于0
  var numberStr = 0;
  assert(numberStr <= 0);  

  // 检查是否为null
  var nullStr;
  assert(nullStr == null);

  // 检查是否为NaN
  var value = 0 / 0;
  assert(value.isNaN);

assert 是Dart语言里的的断言函数,仅在Debug模式下有效
在开发过程中, 除非条件为真,否则会引发异常。(断言失败则程序立刻终止)。

(四)list集合,也成为数组

在Dart中,数组是List对象,因此大多数人只是将它们称为List。
以下是一个简单的Dart的List:

创建一个int类型的list

 

List list = [10, 7, 23];
print(list);// 输出结果  [10, 7, 23]

要创建一个编译时常量const的list,示例如下:

 

List constantList = const[10,3,15];
print(constantList);// 输出结果  [10, 3, 15]

注意事项:

1.可以直接打印list包括list的元素,list也是一个对象。但是java必须遍历才能打印list,java若直接打印list,结果是地址值。
2.和java一样list里面的元素必须保持类型一致,不一致就会报错。
3.和java一样list的角标从0开始。

Dart的list集合给我们提供了很多api,示例如下,api太多就不逐个展示了:

操作代码含义输出结果
新增list.add(1);print(list);把数字1添加到list中,默认是添加到末尾[10, 7, 23, 1]
移除list.remove(1);print(list);移除数字1[10, 7, 23]
插入list.insert(0, 5);print(list);在索引为0的地方插入数字5[5, 10, 7, 23]
查找某个索引的值int value = list.indexOf(10);print(value);查找10在list中的索引1
判断元素是否包含bool result = list.contains(5);print(result);查找list中是否包含数字5true

(五)map集合

Dart中的map是将键和值相关联的对象。键和值都可以是任何类型的对象。每个键只出现一次,但您可以多次使用相同的值。

(1)创建方式:

    1. 直接声明,用{}表示,里面写key和value,每组键值对中间用逗号隔开。

 

Map companys = {'first': '阿里巴巴', 'second': '腾讯', 'fifth': '百度'};
print(companys);//打印结果 {first: 阿里巴巴, second: 腾讯, fifth: 百度}
    1. 先声明,再去赋值。

 

  Map companys1 = new Map();
  companys1['first'] = '阿里巴巴';
  companys1['second'] = '腾讯';
  companys1['fifth'] = '百度';
  print(companys1);
  //打印结果 {first: 阿里巴巴, second: 腾讯, fifth: 百度}
    1. 要创建一个编译时常量const的map,请在map文字之前添加const:

 

final fruitConstantMap = const {2: 'apple',10: 'orange',18: 'banana'};
// 打印结果{second: 腾讯, fifth: 百度, 5: 华为}

(2)添加元素。格式: 变量名[key] = value,其中key可以是不同类型。

 

//添加一个新的元素,key为“5”,value为“华为”
  companys[5] = '华为';
  print(companys);//打印结果 {first: 阿里巴巴, second: 腾讯, fifth: 百度, 5: 华为}

(3)修改元素。格式:变量名[key] = value

例如:把key为first的元素对应的value改成 alibaba

 

  companys['first'] = 'alibaba';
  print(companys);//打印结果 {first: alibaba, second: 腾讯, fifth: 百度, 5: 华为}

(4)查询元素

 

  bool mapKey = companys.containsKey('second');
  bool mapValue = companys.containsValue('百度');
  print(mapKey); //结果为:true
  print(mapValue); //结果为:true

(5)删除元素.可以使用map的remove或者clear方法。

 

  companys.remove('first');// 移除key为“first”的元素。
  print(companys);// 打印结果{second: 腾讯, fifth: 百度, 5: 华为}

  companys.clear();// 清空map集合的数据。
  print(companys);// 打印结果{}

(6)关于map集合的小结:

 

1.创建map有两种方式。
2.map的key类型不一致也不会报错。
3.添加元素的时候,会按照你添加元素的顺序逐个加入到map里面,哪怕你的key不连续。
比如key分别是 1,2,4,看起来有间隔,事实上添加到map的时候{1:value,2:value,4:value} 这种形式。
4.添加的元素的key如果是map里面某个key的英文,照样可以添加到map里面,
比如可以为3和key为three可以同时存在。
5.map里面的key不能相同,但是value可以相同,value可以为空字符串或者为null。

(六)runes 字符(用于在字符串中表示Unicode字符)

Unicode为世界上所有的书写系统中使用的每个字母,数字和符号定义了唯一的数值。
Dart字符串是UTF-16代码单元的序列,所以在字符串中表达32位Unicode值需要特殊的语法。
Unicode代码点的常用方法是\uXXXX,其中XXXX是一个4位十六进制值。

例如,心形字符()是\u2665。要指定多于或少于4个十六进制数字,请将该值放在大括号中。 例如,笑的表情符号是\u{1f600}

String类有几个属性可以用来提取符文信息。 codeUnitAt和codeUnit属性返回16位代码单元。
以下示例说明了符文,16位代码单元和32位代码点之间的关系。

 

var clapping = '\u{1f44f}';
print(clapping);
print(clapping.codeUnits);
print(clapping.runes.toList());

//使用String. fromCharCodes显示字符图形 
Runes input = new Runes(
        '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
print(new String.fromCharCodes(input));

五、运算符

运算符在每一种语言中都很常见,Dart的运算符如下表所示:

我这里不详细去讲解每个运算符的用法,我这里主要讲一下Dart里面比较有代表性的以及有特点的一些运算符相关用法。

(一)?..一样,但最左边的操作数可以为空。

比如:Test?.funs从表达式Test中选择属性funs,除非Test为空(当Test为空时,Test?.funs的值为空)。

 

class Test {
  static int funs = 5;

  Test() {
    print('构造函数 Test');
  }
  static fun() {
    print('Test fun函数');
  }
}
void main(){
  print(Test?.funs); // 打印5
}

(二)..级联符号..

级联符号..允许您在同一个对象上进行一系列操作。 除了函数调用之外,还可以访问同一对象上的字段。其实相当于java的链式调用。
例如:

 

String s = (new StringBuffer()
        ..write('test1 ')
        ..write('test2 ')
        ..write('test3 ')
        ..write('test4 ')
        ..write('test5'))
      .toString();
print(s); // test1 test2 test3 test4 test5

(三)?? 三目运算符的一种形式。

expr1 ?? expr2 表示如果expr1非空,则返回其值; 否则返回expr2的值。

 

//普通三元运算符
int a = 10;
var values = a > 5 ? a : 0;
//??操作符
print('a ??=3 : ${a ??= 3}');  // 3

(四)~/ 除法,返回一个整数结果,其实就是取商。

小学都学过:被除数 ÷ 除数 = 商 ... 余数,在Dart里面A ~/ B = C,这个C就是商,这个语句相当于Java里面的A / B = C。Dart与java不同的是,Dart里面如果使用A / B = D语句,这个结果计算出来的是真实的结果。示例如下:

 

  var result1 = 15/7;
  print(result1); // 结果是:2.142857...
  var result2 = 15~/7;
  print(result2); // 结果是:2

顺便提一下取模操作,在Dart里面A % B = E,这个E就是余数,%符号表示取模,例如:

 

 var result3 = 15%7;
  print(result3); // 结果是:1

(五)as、is与is!

as 判断属于某种类型
is 如果对象具有指定的类型,则为true
is! 如果对象具有指定的类型,则为false

例如:

 

class Test {
  static int funs = 5;

  Test() {
    print('构造函数 Test');
  }
  static fun() {
    print('Test fun函数');
  }
}

class Test2 extends Test {
  Test2() {
    print('构造函数 Test2');
  }
  void fun() {
    print('Test2 fun函数');
  }
}

void main(){
  print(test2 is Test);  // true
  print(test is! Test2);  // true

  (test2 as Test2).fun();  // Test2 fun函数
  // 相当于
  // if (test2 is Test) {
  //   test2.fun();
  // }

六、控制流程语句

控制流程语句和Java语言差不多,有这些语句:

(一)if else

 

if(条件语句){
    内容体
}else{
内容体
}

(二)for循环

1.简单for循环

 

for(初始值;判断条件;循环后的语句){
    内容体
}

例如:

 

for(int i=0;i<10;i++){
    print(i);
}

也可以通过for循环内部的闭包获取索引的值。

 

var array = [];
for(var i=0;i<10;i++){
    array.add(()=> print(i));
}

2.使用foreach循环,一般List和Set都可以使用foreach遍历元素。

如果要迭代的对象是Iterable,或者你不想知道当前的迭代次数,可以使用foreach()方法。

 

var numbers = [1,2,3,4,5,6,7,8,9];
numbers.foreach((number)=> print(number));

3.使用for in循环,一般List和Set使用for-in遍历元素。

 

var list = [1,2,3];
for(var data in list){
    print(data);
}

4.Dart的for循环里面可以使用标记:(比较有特色的地方)

Dart的标记:标记是后面跟着冒号的标识符。带标记的陈述是以标记 L为前缀的陈述。带标签的case子句是标签L前缀的switch语句中的case子句。标签的唯一作用是为“break”和“continue”声明提供对象。
大多数此功能与其他语言类似,因此以下大部分内容可能对读者来说都很熟悉。Dart的switch声明中处理continue是比较独特的,所以这一部分需要一点时间去阅读和熟悉。

  • 1.循环(Loops)

标签最常用作breakcontinue内部循环。假设你有嵌套的循环,并要跳转到breakcontinue到外部循环。如果没有标记,这不可能(轻松)实现。

以下示例使用continue 标记名称从内部循环直接跳转到外部循环的下一轮循环:

 

// 返回具有最小总和的内部列表(正整数)。
/// Returns the inner list (of positive integers) with the smallest sum.
List<int> smallestSumList(List<List<int>> lists) {
  var smallestSum = 0xFFFFFFFF; //已知list的总和较小。
  var smallestList = null;
  outer: // 这就是标记
  for (var innerList in lists) {
    var sum = 0;
    for (var element in innerList) {
      assert(element >= 0);
      sum += element;
      // 无需继续迭代内部列表。它的总和已经太高了。
      if (sum > smallestSum) continue outer; // continue 跳出到标记处(outer)
    }
    smallestSum = sum;
    smallestList = innerList;
  }
  return smallestList;
}

此函数在所有list中运行,但只要总和过高,就会停止累加变量。

同理,可以使用break跳出到外部循环:

 

// 计算第一个非空list
List<int> firstListWithNullValueList(List<List<int>> lists) {
  var firstListWithNullValues = null;
  outer:
  for (var innerList in lists) {
    for (var element in innerList) {
      if (element == null) {
        firstListWithNullValues = innerList;
        break outer;  // break 返回到标记处
      }
    }
  }
  // 现在继续正常的工作流程
  if (firstListWithNullValues != null) {
    // ...
  }
  return firstListWithNullValues;
}
  • 2.跳出代码块

标记也可以用于跳出代码块。假设我们想要统一处理错误条件,但有多个条件(可能是深度嵌套)来揭示(reveal)错误。标签可以帮助构建此代码。

 

void doSomethingWithA(A a) {
  errorChecks: {
    if (a.hasEntries) {
      for (var entry in a.entries) {
        if (entry is Bad) break errorChecks;   // 跳出代码块
      }
    }
    if (a.hasB) {
      var b = new A.b();
      if (b.inSomeBadState()) break errorChecks;  // 跳出代码块
    }
    // 一些看起来都正常
    use(a);
    return;
  }
  // 错误的情况,执行这里的代码:
  print("something bad happened");
}

class A{
  bool hasEntries = false;
  bool hasB = true;
  List<Bad> entries = [new Bad2(),new Bad2()];
  A.b(){

  }

  bool inSomeBadState(){
    return false;
  }
  
}

void use(A a){}

abstract class Bad{}
class Bad1 extends Bad{}
class Bad2 extends Bad{}

对代码块的使用break指令,使得Dart继续执行块之后的语句。从某个角度来看,它是一个结构化的goto,它只允许跳转到当前指令之后的嵌套较少的位置。

虽然声明标签在代码块中最有用,但它们可以用在在每个语句中。
例如,foo: break foo;是一个有效的声明。

请注意:continue上面的循环可以通过将循环体包装到带标记的代码块中并使用break来实现。
也就是说,以下两个循环是等效的:

 

//以下两种描述是等价的:

// 使用 continue
for (int i = 0; i < 10; i++) {
  if (i.isEven) continue;
  print(i);
}

// 使用 break.
for (int i = 0; i < 10; i++) {
  labels: {
    // isEven 当且仅当该整数为偶数时才返回true
    if (i.isEven) break labels;
    print(i);
  }
}
  • 3.Switch中的标记(label)

标记也可以用于switch内部。
Switch中的标记允许continue 使用其它的case 子句。在最简单的形式中,这可以作为一种方式来实现下一个子句:

 

void switchExample(int foo) {
  switch (foo) {
    case 0:
      print("foo is 0");
      break;
    case 1:
      print("foo is 1");
      continue shared; // Continue使用在被标记为shared的子句中
    shared:
    case 2:
      print("foo is either 1 or 2");
      break;
  }
}

有趣的是, Dart没有要求continue的目标子句是当前case子句之后的子句。
带标记的任何case子句都是有效的目标。这意味着,Dart的switch语句实际上是状态机(state machines)。

以下示例演示了这种滥用,其中整个switch实际上只是用作状态机(state machines)。

 

void main() {
  runDog();
}

void runDog() {
  int age = 0;
  int hungry = 0;
  int tired = 0;

  bool seesSquirrel() => new Random().nextDouble() < 0.1;
  bool seesMailman() => new Random().nextDouble() < 0.1;

  switch (1) {
    start:
    case 0:
      print("dog 方法已经开始");
      print('case 0 ==> age: $age');
      print('case 0 ==> hungry: $hungry');
      print('case 0 ==> tired: $tired');
      continue doDogThings;

    sleep:
    case 1:
      print("sleeping");
      tired = 0;
      age++;
      if (age > 20) break;
      print('case 1 ==> age: $age');
      print('case 1 ==> hungry: $hungry');
      print('case 1 ==> tired: $tired');
      continue doDogThings;

    doDogThings:
    case 2:  
      if (hungry > 2) continue eat;
      if (tired > 3) continue sleep;
      if (seesSquirrel()) continue chase;
      if (seesMailman()) continue bark;
      print('case 2 ==> age: $age');
      print('case 2 ==> hungry: $hungry');
      print('case 2 ==> tired: $tired');
      continue play;

    chase:
    case 3:  
      print("chasing");
      hungry++;
      tired++;
      print('case 3 ==> age: $age');
      print('case 3 ==> hungry: $hungry');
      print('case 3 ==> tired: $tired');
      continue doDogThings;

    eat:
    case 4:  
      print("eating");
      hungry = 0;
      print('case 4 ==> age: $age');
      print('case 4 ==> hungry: $hungry');
      print('case 4 ==> tired: $tired');
      continue doDogThings;

    bark:
    case 5: 
      print("barking");
      tired++;
      print('case 5 ==> age: $age');
      print('case 5 ==> hungry: $hungry');
      print('case 5 ==> tired: $tired');
      continue doDogThings;

    play:
    case 6: 
      print("playing");
      tired++;
      hungry++;
      print('case 6 ==> age: $age');
      print('case 6 ==> hungry: $hungry');
      print('case 6 ==> tired: $tired');
      continue doDogThings;
  }
}

这个函数从一个switch子句跳到另一个子句,模拟了狗的生命。
在Dart中,标签只允许在case子句中使用,因此我必须添加一些case永远不会到达的行。

这个功能很酷,但很少使用。由于我们的编译器增加了复杂性,我们经常讨论它的删除。到目前为止,它已经在我们的审查中幸存下来,但我们最终可能会简化我们的规范并让用户自己添加一个while(true)循环(带有标记)。这个dog的示例可以重写如下:

 

var state = 0;
loop:
while (true)
  switch (state) {
    case 0:
      print("dog has started");
      state = 2; continue;

    case 1:  // sleep.
      print("sleeping");
      tired = 0;
      age++;
      // The inevitable... :(
      if (age > 20) break loop;  // 跳出循环
      // Wake up and do dog things.
      state = 2; continue;
    
    case 2:  // doDogThings.
      if (hungry > 2) { state = 4; continue; }
      if (tired > 3) { state = 1; continue; }
      if (seesSquirrel()) { state = 3; continue; }
      ...

如果状态值被命名为常量,那么它将与原始版本一样具有可读性,但不需要switch语句来支持状态机。

(三)while 和do while

 

while(判断条件){
    内容体
}

 

do{
内容体
} while(判断条件);

 

while(a>5){
    print(a);
}

 

do{
print(a);
} while(a>5);

(四)break continue

break 停止循环

 

while(a>5){
  if(b>5){
  print(a);
    break;
  }
}

continue 跳到下一个循环

 

while(a>5){
  if(b<10){
  print(b);
    continue;
  }
}

如果使用Iterable(list或者set),则可以使用下面这种方式:

 

// 第一个是满足条件就进入  第二个是foreach遍历
arrays
  .when((c)=>c.counts >=5)
  .foreach((c)=>c.next());

(五)switch case

比较integer, string,编译时常量 使用==。比较对象必须是同一个类的实例(不是其子类的实例),并且该类不能重写==。枚举类型在switch也可以运行。
每一条非空case字子句以break结束,也可以使用其他的方式结束:continue,throw或者return

 

var command = 'OPEN';
switch (command) {
  case 'CLOSED':
    executeClosed();
    break;
  case 'PENDING':
    executePending();
    break;
  case 'APPROVED':
    executeApproved();
    break;
  case 'DENIED':
    executeDenied();
    break;
  case 'OPEN':
    executeOpen();
    break;
  default:
    executeUnknown();
}

(六)assert

如果布尔条件为false,则使用assert语句来中断正常执行。例如:

 

// 确保变量具有非空值 
assert(text != null);
// 确保值小于100
assert(number < 100);
// 确保这是一个 https 网址
assert(urlString.startsWith('https'));

要将消息附加到断言,请添加一个字符串作为第二个参数。

 

assert(urlString.startsWith('https'),'URL ($urlString) should start with "https".');

上例中assert的第一个参数可以是任何解析为布尔值的表达式。如果表达式的值为true,则断言成功并继续执行。如果为false,则断言失败并抛出异常


七、异常

Dart代码可以抛出并捕获异常。Exception是指示发生意外事件的错误。如果未捕获异常,则会暂停引发异常的isolate ,并且通常会终止isolate及其程序。

与Java相比,Dart的所有异常都是未经检查的异常。方法不会声明它们可能引发的异常,并且您不需要捕获任何异常。

Dart提供了ExceptionError 类型,以及许多预定义的子类型。当然,您可以定义自己的Exception。但是,Dart程序可以抛出任何非null对象,作为Exception(不仅仅是Exception和Error对象)。

(一)throw

以下是抛出或引发异常的示例:

 

throw FormatException('Expected at least 1 section');

你也可以抛出任意对象,例如:throw '格式不正确!';
通常在开发中会抛出Error或者Exception类型。

因为抛出异常是一个表达式,所以可以在=>语句中以及允许表达式的任何其他地方抛出异常:

 

void distanceTo(Point other) => throw UnimplementedError();   

(二)try catch

捕获或捕获异常会阻止异常传递(除非您重新抛出异常)。捕获异常使您有机会处理它:

 

try {
    breedMoreLlamas();
} on OutOfLlamasException {
    buyMoreLlamas();
}

要处理可能抛出多种类型异常的代码,可以指定多个catch子句。与抛出对象的类型匹配的第一个catch子句处理异常。如果catch子句未指定类型,则该子句可以处理任何类型的抛出对象。
您可以使用on或catch两者兼而有之。使用on时需要指定异常类型。使用catch时,你的异常处理程序需要异常对象。
示例:

 

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // A specific exception
  buyMoreLlamas();
} on Exception catch (e) {
  // Anything else that is an exception
  print('Unknown exception: $e');
} catch (e) {
  // No specified type, handles all
  print('Something really unknown: $e');
}

您可以指定一个或两个参数catch()。第一个是抛出的异常,第二个是堆栈跟踪(StackTrace对象)。
示例:

 

try {
  // ···
} on Exception catch (e) {
  print('Exception details:\n $e');
} catch (e, s) {
  print('Exception details:\n $e');
  print('Stack trace:\n $s');
}

要部分处理异常,同时允许它传递,请使用rethrow关键字。
示例:

 

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); // 运行时异常
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // 允许调用者查看exception.
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.');
  }
}

(三)finally

无论是否抛出异常,要确保某些代码运行,请使用finally子句。如果没有catch子句匹配该异常,则在finally子句运行后传递异常。
示例:

 

try {
  breedMoreLlamas();
} finally {
  // 即使抛出异常  也会执行这句代码.
  cleanLlamaStalls();
}
该finally子句在任何匹配的catch子句之后运行:
try {
  breedMoreLlamas();
} catch (e) {
    // 首先会处理异常
  print('Error: $e'); 
} finally {
  // 然后执行这句语句
  cleanLlamaStalls(); 
}



作者:AWeiLoveAndroid
链接:https://www.jianshu.com/p/3d927a7bf020
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值