一、Dart基本特色概念
所有变量的值都是对象,也就是类的实例。数字、函数和null也都是对象,都继承自Object类。
- 虽然Dart是强类型语言,但Dart支持类型推断,显示变量类型声明是可选的。没有指定类型的变量的类型为dynamic。可以通过使用类型或者编译时常量来帮助Dart去捕获异常以及让代码运行更高效。
Dart支持泛型,List表示包含int类型的列表,List则表示包含任意类型的列表。(List等同于List)
- Dart支持顶层(Top-level)函数(例如:main()),也支持类中定义函数(静态函数和实例函数),也支持嵌套函数和本地函数。
Dart支持顶层变量和类成员变量。
- Dart没有public、protected和private这些关键字,使用下划线_开头的变量或函数,表示库内可见。
二、语法预览
//程序入口函数main() { //42,字面量,编译时常量 var number = 42; // 定义的一个变量,dynamic printNumber(number); print('number ${number.runtimeType}'); //打印number的运行时类型}printNumber(num number) { print('The number is $number');// return null;如果函数没有返回值,此处默认返回null}//打印结果The number is 42number int
(1)所有的函数都返回一个值。如果没有指定返回值,则默认把return null
作为函数的最后一个语句执行。(2)main()
方法是Dart程序执行的入口方法,每个程序都需要一个这样的方法。 1.main()
方法签名的void
可有可无。 2.main()
方法有一个可选的List
参数,此参数在Android studio运行时配置,如图:(1)var
一种不指定类型声明变量的方式。
1.Dart中一切皆对象,所以没有初始化的默认值都是null
,null
是Null类的唯一实例。
2.我们也可以给变量申请一个具体类型,这样有助于编译工具帮我们补全代码,查找bug。如:String name = 'Bob';
3.在代码风格中,推荐在编写严格API时尽量使用类型声明(规定使用),编写独立应用时尽量使用var声明(快速开发)。
4.想要知道具体类型,使用runtimeType
。
(2)var number = 42;
中42 是一个字面量。字面量是编译时常量。
(3)num是一个类型(类型接口,子类有int
和double
)。
(4)print()
是打印语句,参数可以是a+b,a,” “,’ '都行。
三、内置类型
Numbers(数值)
(1)int
和double
都是数值类型,都是num
的子类。 1.int
是整数型,取值范围在-2^53 和 2^53 之间。1, 2,3 ,4 … 2.double
是浮点型 , 64位。1.2324, …(2)num
类型定义了基本的操作符,例如+ ,- , *, / ,= ,<=等等,还定义了abs()
,ceil()
,floor()
等函数。int i = 1;double d = 1.2;num j = 1;num k = 1.2;
String字符串
(1)字符串可以使用双引号" ",单引号’ ‘或三个单引号’’’ ‘’’。
1.双引号和单引号都是定义单行的字符串
2.三个单引号连用的可以定义多行的字符串类型
3.Dart 的String 是 UTF-16 编码的一个队列。
String str1 = 'abc';//单引号字符串String str2 = "abc";//双引号字符串//三个', ' 或 " (不能混合使用)来定义多行的String类型,注意str3和str4的显示区别String str3 = '''abc def''';String str4 = "abc" "def";String str5 = "abc"+"def";//str4和str5是一样的print("str1 = $str1");//abcprint("str2 = $str2");//abcprint("str3 = $str3");//abc \n def 此处是两行 中间的回车和空格也被打印出来了print("str4 = $str4");//abcdef 此处忽略了回车和空格print("str5 = $str5");//abcdef
(2)r前缀是忽略转义符,创建一个”原始raw“的字符串。可以打印完整的url。
//通过提供一个r前缀可以创建一个 “原始 raw” 字符串,原始字符串中没转义字符var s = r"In a raw string, even \n isn't special."print("s = $s");//打印结果:In a raw string, even \n isn't special
(3)字符串与数值之间的转换
// String -> intvar one = int.parse('1');// String -> doublevar onePointOne = double.parse('1.1');// int -> StringString oneAsString = 1.toString();// double -> String//此处是小数点后保留两位,四舍五入String piAsString = 3.14159.toStringAsFixed(2);piAsString = '3.14';
Boolean(布尔值)
(1)只有true和false两个值,但只有true对象还被认为是true。(2)使用bool
的类型bool flagTrue = true ;bool flagFalse = false ;if(1){}//错误不可这么写if(1 != 0){}if(true){}if(false){}
Lists(列表)
(1)List中可指定类型,如List,List等,也可不指定类型,此时默认为任意型,可混合保存(2)List可以直接定义一个具体的类别,也可以new一个实例,然后添加。
(3)其中的元素可以使用runtimeType查看运行时的类型。
(4)List类中提供各种API,类似java中的list。
(5)遍历元素 使用forEach(void f(E element))
//直接定义int类型的列表List list1 = [1, 2, 3];//直接定义String类型的列表List list2 = ['a', 'b', 'c'];//直接定义可存放任意类型的列表var list3 = [1, 'a', 3];//new出list实例,可添加元素var list4 = new List();list4.add(4);list4.add("dart");list4.add(6);//列表中一些常用方法list1.[1];//2list2.length;//3[].length;//0[].isEmpty;//true['a'].isEmpty;//falseprint('list1 = $list1');//list1 = [1, 2, 3]print('list2 = $list2');//list2 = [a, b, c]print('list3 = $list3');//list3 = [1, a, 3]print('list4 = $list4');//list4 = [4, dart, 6]print('${list3[0].runtimeType},${list3[1].runtimeType}');//int,String//runtimeType,返回(运行时类型)对象的类型//遍历元素 使用forEach(void f(E element)) list4.forEach((a){ print(a); });//打印结果4dart6
map
(1)map是键值对形式的集合。键可以是任意类型。(2)遍历元素 使用forEach(void f(K key, V value))
//直接定义// Keys :Valuesvar colors1 = { 'first' : 'red', 12: 'green', 'third' : 'blue'};print('colors1 = $colors1');//colors1 = {first: red, 12: green, third: blue}print(colors1[12]);//greenvar colors2 = new Map();//中括号内为key,等号右为值colors2['first'] = 'red';colors2['second'] = 'green';colors2['third'] = 'blue';//通过map中的key获取value,如果所查找的key不存在,则返回 null:print('colors2['first']=${colors2['first']}');//'colors2['first']=red//获取长度print('colors2.length = ${colors2.length}');//colors2.length = 3//遍历元素colors2.forEach((key,value){ print('key = $key;value = $value'); });//打印结果key = first;value = redkey = second;value = greenkey = third;value = blue
Runes符号文字 (不常用)
(1)Dart语言中String字符串是一系列UTF-16代码单元,Sting的codeUnits和codeUnitAt属性可以获取UTF-16字符集的字符。(2)通常使用 \uXXXX 的方式来表示 Unicode code point,这里的 XXXX 是4个 16 进制的数。例如,心形符号 (♥) 是 \u2665。对于非 4 个数值的情况,把编码值放到大括号中即可。例如,笑脸 emoji (?) 是 \u{1f600}。
(3)如果String字符串中需要表示32位Unicode值需要使用runes来获取UTF-32的字符集的字符。
(4)runes代表字符串的 UTF-32 code points。
var clapping = '\u{1f44f}'; print(clapping); print(clapping.codeUnits);//16-bit print(clapping.codeUnitAt(1)); print(clapping.runes.toList());//32-bit Runes input = new Runes( '\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}'); print(new String.fromCharCodes(input));//打印结果?[55357, 56399]56399[128079]♥ ? ? ? ? ?
注意:使用 list 操作 runes 的时候请小心。根据所操作的语种、字符集等,这种操作方式可能导致你的字符串出问题。
##Symbols标志(基本用不上)
一个 Symbol object 代表 Dart 程序中声明的操作符或者标识符。你也许从来不会用到 Symbol,但是该功能对于通过名字来引用标识符的情况 是非常有价值的,特别是混淆后的代码, 标识符的名字被混淆了,但是 Symbol 的名字不会改变。使用 Symbol 字面量来获取标识符的 symbol 对象,也就是在标识符 前面添加一个 # 符号:
#radix#bar
Symbol 字面量定义是编译时常量。四、Functions(方法)
(1)基本特点
1.Dart中方法也是一个对象,类型为Function。 2.方法可以赋值给变量,也可以当做其他方法的参数,也可把Dart类的实例当做方法来调用。 3.方法有返回值,如果没有指定返回值,默认将return null;作为最后语句返回null。 4.main()方法是程序的入口方法。 5.Dart中没有final方法,几乎所有方法都允许重写(部分内置的操作符除外) 6.当方法只有一个表达式时,可以使用**=>**缩写。 7.方法中的返回类型和参数类型可以省略。//平时使用方式String sayHello1(String name){ return 'Hello $name!'; //return print('Hello $name!');//print();方法返回的是void}//这样也是可以的,忽略类型定义sayHello2(name){ return 'Hello $name!';}//对于只有一个表达式的方法,你可以选择使用缩写语法来定义:sayHello3(name) => 'Hello $name!';//注意:在箭头 (=>) 和冒号 (;) 之间只能使用一个表达式 –- 不能使用语句。表达式计算后通常会返回一个单独的值.//例如下面这些,注释后面的就是表达式:int i = 10;//i = 10anArray[0] = 10;//anArray[0] = 100int result = 1 + 2; // result = 1 + 2if (value1 == value2)//value1 == value2//方法也可以赋值给一个变量--类似函数指针var sayHello4 = (name)=>'Hello $name!';
(2)函数闭包一个 闭包 是一个方法对象,不管该对象在何处被调用, 该对象都可以访问其作用域内 的变量。方法可以封闭定义到其作用域内的变量。下面的示例中,makeAdder() 捕获到了变量 addBy。不管你在那里执行 makeAdder() 所返回的函数,都可以使用 addBy 参数。//返回值为FunctionFunction makeAdder(num addBy) { return (num i) => addBy + i;}main() { //方法可以赋值给变量, //此时将makeAdder的返回值(num i) => addBy + i 赋值给add2和add4 var add2 = makeAdder(2);//add2(num i) => 2 + i; var add4 = makeAdder(4);//add2(num i) => 4 + i; //断言 判断内容是否正确,错误会抛出异常 assert(add2(3) == 5);//add2(3)=5 assert(add4(3) == 7);//add4(3)=7}
(3)typedef 别名1.typedef 是方法的简单的别名,它提供了一种方法来检查任何函数的类型。
2.typedef定义的别名方法有返回值,那么定义的方法不仅要参数匹配,返回值也要一样,否则或报错。
3.typedef定义的别名方法没有返回值,那么只要参数匹配就可以了。
定义typedef的别名方法也可以使用泛型。
typedef int Compare(int a, int b);typedef Fun1(int a, int b);typedef Fun(T a, K b);int sort(int a, int b) => a - b;main() { assert(sort is Compare); // True!}
typedef int Compare(int a, int b);typedef Fun1(int a, int b);typedef Fun2(T a, K b);int add(int a, int b) { print('a * b'); return a * b;} add1(int a, int b) { print('a + b'); return a + b;} add2(String a, int b) { print('a = b'); return 'a = ' + b.toString();}class Demo1 { Demo1(int f(int a, int b), int x, int y) { var sum = f(x, y); print("sum1 :$sum"); }}class Demo2 { Demo2(Fun1 f, int x, int y) { var sum = f(x, y); print("sum2 :$sum"); }}class Demo3 { Demo3(Fun2int> f, String x, var sum = f(x, y); print("sum3 :$sum"); }}goTypedef() { Demo1 d1 = new Demo1(add, 2, 3); Demo2 d2 = new Demo2(add1, 5, 6); Demo3 d3 = new Demo3(add2, 'a', 6);}main() { goTypedef();}//打印结果a * bsum1 :6a + bsum2 :11a = b sum3 :a = 6
目前,typedef 只能使用在 function 类型上。(4)可选参数的方法
1.可选参数可分为:可选命名的和可选位置的 2.方法中参数可以有默认值。赋默认值的方式两种:等号’=‘或者冒号’:’,默认值至少编译时常量。a、可选命名参数
1.在定义时,使用大括号内加参数{paramName param1,param2...}
的形式定义参数,类型可有可无。 2.在调用时,使用paramName : value
来指定命名参数。可指定任意参数,没有顺序要求。//定义可选命名参数的方法//赋默认值的方式两种:等号'='或者冒号':',FunA(bool a, {b, c:3, d=4, e}){ print('a = $a,b = $b,c = $c,d = $d,e = $e');}void main(){ //调用 FunA(true,b:2,e:5); FunA(false,b:"one",c:5,);}//打印结果a = true,b = 2,c = 3,d = 4,e = 5a = false,b = one,c = 5,d = 4,e = null
b、可选位置参数
1.在定义时,使用中括号内加参数[paramName param1,param2…]`的形式定义参数,类型可以有可无。 2.在调用时,直接传入参数就行,但是参数位置是按顺序赋值的,如果要给最后一个参数传值,前面的参数也要传值。//定义可选位置//赋默认值只有一种方式:等号'='FunB(a, [b, c=3, d=4, e]){ print('a = $a,b = $b,c = $c,d = $d,e = $e');}//调用main() { FunB(true,2, 5); FunB(false,"one",5,6,7);}//打印结果a = true,b = 2,c = 5,d = 4,e = nulla = false,b = one,c = 5,d = 6,e = 7
c、以List,map作为默认参数
void doStuff( {Listlist = const [1, 2, 3], Map gifts = const { 'first': 'paper', 'second': 'cotton', 'third': 'leather' }}) { print('list: $list'); print('gifts: $gifts');}main() { doStuff();}//打印结果list: [1, 2, 3]gifts: {first: paper, second: cotton, third: leather}
(5)方法可作为参数被调用
//定义一个普通方法printNumber(name) { print(name);}main() { var list = [1, 2, 3]; //可以把方法当做参数调用另外一个方法 list.forEach(printNumber); //匿名方法 list.forEach((i){ print(i); });}//打印结果都是 1 2 3
五、操作符
(1)Dart中定义的操作符下表为Dart中定义的操作符,很多操作符是可以重载的。
描述 | 操作符 | 解释 |
unary postfix 一元后缀 | expr++ 自增 expr-- 自减 () 调用方法 [] 访问列表元素 . 访问元素 ?. 条件非空判断 | 用在表达式后 |
unary prefix 一元前缀 | -expr 负号 !expr 取反 ~expr 补码++expr 自增 --expr 自减 | 用在表达式前 |
基本运算符 | + 加 - 减 * 乘 / 除,除不尽就显示小数% 取模 ~/ 除后取z整 | ~/ 与java中/ 效果一样,其余与java中操作符运算一样 |
Bitwise and shift operators(位和移位操作符) | << 左移 >> 右移 & 位与 | 位或 ^ 位异或 | 与java中操作符运算一样 |
Equality and relational operators(相等相关的操作符) | >= 大于等于 > 大于 <= 小于等于 < 小于== 等于 != 不等于 | 与java中操作符运算一样 |
Assignment operators(赋值操作符) | = 直接赋值 *= 乘后赋值 >>= 右移后赋值~/= 取余后赋值 %= 取模后赋值 += 加后赋值-= 减后赋值 <<= 左移后赋值/= 除后赋值&= 位与后赋值 ^= 异或后赋值 |= 位或后赋值 ??= 非空判断后赋值 | ??= java中没有,Dart中特有的 |
Type test operators(类型判定操作符) | as 、 is 、 is! | 都是java中没有,Dart中特有的 |
Logical operators(逻辑操作符) | !expr 取反 || 逻辑或 && 逻辑与 | 与java中操作符运算一样 |
if null 如果空判断 | ?? | 效果与三目表达式一样 |
conditional 三目 | expr1 ? expr2 : expr3 | 与java中操作符运算一样 |
Cascade notation (级联操作符) | .. | java中没有,Dart中特有的 |
2、Dart中特有的操作符
(1)as,is,is! 类型判断操作符
操作符 | 解释 |
as | 类型转换 (把左边对象转换为右边特定的类型) |
is | 如果对象是指定的类型返回 True(检查is左边的类型是否是右边的类型) |
is! | 如果对象是指定的类型返回 False(检查is左边的类型是否是右边的类型) |
is
和is!
时,只有obj
实现了T
的接口,obj is T
才是true。 2.使用as
时,如果左边obj
未实现右边T
,则使用obj as T
会抛异常。所以在使用as
时要先使用is
判断类型。if (emp is Person) { // Type check,如果emp不是Person子类,是false,就不会跳进 emp.firstName = 'Bob';}//如果emp为null不是Person类型,则(emp as Person)就会抛出异常(emp as Person).firstName = 'Bob';//建议这样书写if(emp is Person){ (emp as Person).firstName = 'Bob';}
(2) ??=??=
操作符用来指定值为 null 的变量的值。
main() { var b; b = 2; //给 b 变量赋值 print('给b赋值后 b = $b'); b ??= 1; // 如果 b 是 null,则将1赋值给 b;如果b不是 null,则 b 的值保持不变 print('b ??= 1操作后 b = $b'); b = null; print('将b置为null后 b = $b'); b ??= 1; print('b ??= 1操作后 b = $b');}//打印结果给b赋值后 b = 2b ??= 1操作后 b = 2将b置为null后 b = nullb ??= 1操作后 b = 1
(3)?? 效果类似三目运算
b ??= value; // 如果 b 是 null,则赋值给 b;如果不是 null,则 b 的值保持不变
String toString() => msg ?? super.toString();//两个表达式效果相同String toString() => msg == null ? super.toString() : msg;
(4)?.和.类似,但是左边的操作对象不能为 null,例如foo?.bar 如果 foo 为 null 则返回 null,否则返回 bar 成员
class A { var bar = 1;}main() { A foo; print('foo?.bar =${foo?.bar}'); foo= A(); print('foo?.bar =${foo?.bar}');}//打印结果foo?.bar =nullfoo?.bar =1
(5) …级联语法
- 级联操作符 (
..
) 可以在同一个对象上连续调用多个函数以及访问成员变量。 - 使用级联操作符可以避免创建临时变量,并且写出来的代码看起来更加流畅:
- 在方法上使用级联操作符需要非常小心,无法在返回值为
void
上使用级联操作符
class Person { String name; String country; void setCountry(String country){ this.country = country; } String toString() => 'Name:$name\nCountry:$country';}void main() { Person p = new Person(); p ..name = 'Wang' ..setCountry('China'); print(p);}//打印结果:Name:WangCountry:China
六、控制语句
##1、if and else 判断语句与其他语言的if else使用方式一样。
在Dart语言中注意对
true
的定义,只有true对象才被定义为true。
if (isRaining()) { you.bringRainCoat();} else if (isSnowing()) { you.wearJacket();} else { car.putTopDown();}
##2、for loops for循环
可以使用标准的for循环
for (var i = 0; i < 5; i++) { ......}
Dart中for循环的闭包可以自己捕获循环的index索引值。获取索引对应的值。
var letters = ["a",'b','c'];letters.forEach((letter){ print('letter = $letter'); });//打印结果letter = aletter = bletter = c
List 和 Set 等实现了 Iterable 接口的类还支持for-in形式的遍历。
List letters = ["a",'b','c'];for(var letter in letters){ print('letter = $letter'); }//打印结果letter = aletter = bletter = c
##3、while and do-while loops while循环- while循环,先判断条件,为true进入循环,否则跳过。
int a = 1;while(a < 5){ print('a = $a'); a++;}//打印结果a = 1a = 2a = 3a = 4
do…while循环,先执行循环代码,然后再判断条件。即此循环至少能执行一次循环语句。
##4、break and continue 控制语句
使用break来终止循环
main() { var a = 0 ; while(true){ print('a = $a'); if(a > 2){ print('a = $a 时终止循环'); break; } a++; }}//打印结果a = 0a = 1a = 2a = 3a = 3 时终止循环
使用 continue 来开始下一次循环:
main(){ for (int i = 0; i < 4; i++) { if(i ==2 ){ print('当i = $i时,跳过此处操作。'); continue; } print('i = $i'); }}//打印结果i = 0i = 1当i = 2时,跳过此处操作。i = 3
##5、switch case 1.Dart中switch语句是通过==来和case后的int ,String或者编译时常量的。 2.每个非空的case语句都必须有一个break语句。另外也可以通过continue,throw或者return来结束非空的case语句。空的case语句可以省略break语句。 3.当没有case语句匹配的时候,可以使用default语句来匹配这种默认情况。 4.case都必须是编译时常量,这些常量必须符合以下条件:- 都是int的实例
- 都是String的实例
- 都是同一个类的实例且该类必须从Object继承了==的实现
- switch使用比较,class必须没有覆写操作符
var flag = 1; switch (flag) { case 0: // break;//当注释此处的break,则不会报错,此时flag=0时会继续往下执行 case 1的逻辑 case 1: print('flag = $flag'); break; //挡注释调此处break时,编译时会报错 case 2: print('flag = $flag'); break; default://此时flag没有匹配到case语句 print('flag = $flag'); break; }
5.当switch语句中使用枚举时,在case语句中要把枚举中每个值都写上,或者使用default,否则会报错。
enum Color { red, green, blue }Color color = Color.red;switch (color) { case Color.red: print("color.red"); case Color.blue: print("color.blue"); break; //当注释到以下内容,编译时会报错error: Missing case clause for 'green'. //case Color.green: // print("color.green"); // break; //default:}
6.在执行了一个case语句后,还想继续执行,可以使用continue语句跳到对应标签继续执行。enum Color { red, green, blue }main() { Color color = Color.red; switch (color) { case Color.red: print("color.red"); continue goGreen;//定义一个标签,名字随意 case Color.blue: print("color.blue"); break; goGreen://此处标签要与continue对应 case Color.green: print("color.green"); break; default: }}//打印结果color.redcolor.green
##6、assert 断言- assert 方法的参数可以为任何返回布尔值的表达式或者方法。如果返回的值为 true,断言执行通过,执行结束。如果返回值为 false,断言执行失败,会抛出一个异常 AssertionError)。
- 如果条件表达式结果不满足需要,则可以使用
assert
语句俩打断代码的执行。
int lineCount;assert(lineCount == null);//断言//如果条件不为 true 则会抛出一个AssertionError异常。
七、异常Exceptions
- Dart中异常是非检查异常,方法不一定声明了他们所抛出的异常,并且不要钱捕获任何异常。
- 如果异常没有捕获,则异常会抛出,导致抛出异常的代码终止执行。
- Dart中
throw
可以抛出任意非null对象作为异常,不仅是提供的Exception
和Error
类型。 - 你可以使用on 或者 catch 来声明捕获语句,也可以同时使用。使用 on 来指定异常类型,使用 catch 来捕获异常对象。
- 使用
rethrow
关键字可以 把捕获的异常给 重新抛出
void goThrow(num a) { try { getName2(a); print('继续执行'); } catch (e, s) { //函数 catch() 可以带有一个或者两个参数, 第一个参数为抛出的异常对象, // 第二个为堆栈信息 (一个 StackTrace 对象)。 print("goThrow catch = $e\n$s"); } finally { print('goThrow finally'); }}getName2(num a) { //你可以使用on 或者 catch 来声明捕获语句,也可以同时使用。 // 使用 on 来指定异常类型,使用 catch 来捕获异常对象。 try { getName(a); } on Exception { print("getName2:Exception"); } on NullThrownError catch (e) { print("getName2 on = $e"); //使用 rethrow 关键字可以 把捕获的异常给 重新抛出。 rethrow; } catch (e) { // 没指定类型,捕获任何异常类型 print("getName2 catch $e"); }}getName(num a) { if (a ==0) { throw new NullThrownError(); }else if(a == 1){ throw "不为0"; } print('准备执行');}main() { goThrow(1); //goThrow(0); //goThrow(3);}//goThrow(1);打印结果getName2 catch 不为0继续执行goThrow finally//goThrow(0);打印结果getName2 on = Throw of null.goThrow catch = Throw of null.#0 getName (file:///D:/project/flutter_app/lib/demo1.dart:36:5)#1 getName2 (file:///D:/project/flutter_app/lib/demo1.dart:21:5)#2 goThrow (file:///D:/project/flutter_app/lib/demo1.dart:6:5)#3 main (file:///D:/project/flutter_app/lib/demo1.dart:44:3)#4 _startIsolate. (dart:isolate/runtime/libisolate_patch.dart:289:19)#5 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)goThrow finally//goThrow(3);打印结果 准备执行继续执行goThrow finally