携手创作,一同生长!这是我参加「日新计划 8 月更文挑战」的第1天,点击查看活动详情
前言
很高兴遇见你~
在本系列的上一篇文章中,咱们介绍了 Flutter 开发环境搭建,以及运用 AndroidStudio 运转你的榜首个 Flutter 项目,体会了热重载。还没有看过上一篇文章的朋友,主张先去阅览Flutter 系列(一):运转你的榜首个 Flutter 运用,在我看来,Dart 在设计时应该是借鉴了百家言语之所长:Java,Kotlin等:
1、在静态语法方面,如:类型界说,办法声明,泛型等,和 Java 十分相似
2、一些语法特性,如:函数式特性,空安全,函数默许值等,和 Kotlin 十分相似
3、Dart 还有一些自己独创的语法,如:命名结构办法,级联操作符等
总之,了解之后,你会发现 Dart 是一门十分有意思的编程言语,接下来就让咱们一同进入 Dart 的语法学习吧
留意: Dart 语法和 Java,Kotlin 真的很像,尤其是 Java。其他假如对 Kotlin 语法不熟的,可以去看我的其他一篇文章:”Kotlin”系列: 一、Kotlin入门
一、变量和办法
1.1、变量
1)、Dart 可以显现指明类型来声明一个可变的变量。且指明的类型分为可空和非空
2)、Dart 也可以运用 var 关键字来声明一个可变的变量,此刻编译器会依据变量初始值主动揣度类型
3)、Dart 运用 final 关键字来声明一个不行变的变量,且可以替代 var 或加在类型前面
4)、Dart 中变量假如对错空类型,那么有必要给一个默许值,不然无法编译经过。假如是可空类型,默许值都为 null
5)、Dart 中每一行代码都要加 ; ,走回头路了,有点鸡肋
//1、Dart 可以显现指明类型来声明一个可变的变量。且指明的类型分为可空和非空
//1.1、类比 Java,显现指明类型来声明一个可变的变量
//dart 写法:
int a = 10;
bool b = true;
//Java 写法
int a = 10;
boolean b = true;
//1.2、指明的类型分为可空和非空
//dart 写法
int? a = 10;
bool? b = true;
//kotlin 写法
var a: Int? = 10
var b: Boolean? = true
//2、Dart 也可以运用 var 关键字来声明一个可变的变量,此刻编译器会依据变量初始值主动揣度类型
//dart 写法
var a = 10;
var b = true;
//kotlin 写法
var a = 10
var b = true
//3、Dart 运用 final 关键字来声明一个不行变的变量,且可以替代 var 或加在类型前面
//dart 写法
final a = 10;
final int aa = 10;
final b = true;
final bool bb = true;
//Java 写法
final int a = 10;
final boolean b = true;
//5、Dart 中变量假如对错空类型,那么有必要给一个默许值。假如是可空类型,默许值都为 null
void main() {
String s = "erdai";
int? i;
print('$s $i'); //打印成果:erdai null
}
小主张:界说变量,优先运用主动揣度,来自 Dart 官方的主张
留意: Dart 彻底扔掉了 Java 中的根本数据类型,全部都是目标数据类型
5)、Dart 中还可以运用 Object 和 dynamic 关键字来声明一个变量
//1、Dart 中还可以运用 Object 和 dynamic 关键字来声明一个变量
//1.1、Object 声明变量,这一点和 Java 没任何差异
Object a = 10;
Object b = true;
Object str = "erdai666";
//1.2、dynamic 声明变量,这一点是 Java 所没有的
dynamic a = 10;
dynamic b = true;
dynamic str = "erdai666";
考虑一个问题:Object 和 dynamic 有啥差异呢?️
答:Object 是一切类的基类,相当于一个可以兼容一切类型的超级类型,这点和 Java 相似。dynamic 便是一个界说动态类型的关键字
//1、声明一个 Object 类型的变量调用 substring,此刻会编译报错,由于 Object 没有 substring 办法
Object str = "erdai666";
str.substring(1); //编译报错:The method 'substring' isn't defined for the type 'Object'.
//2、运用 dynamic 界说一个变量调用 substring,此刻可以绕过编译查看
dynamic str = "erdai666";
str.substring(1);
留意:运用 dynamic 界说的变量调用相关指定类型 api 时,由于会绕过编译器查看,所以别写错了,不然运转时就会报找不到此 api,如下:
可以看到,编译器提示: String 类没有 subString 办法。便是由于咱们 api 写错了,将 substring 写成了 subString 导致的
1.2、常量
1)、Dart 运用 const 关键字来界说一个常量
2)、Dart 可以运用 const 关键字替代 var 或加在类型前面
3)、Dart 还可以运用 const 关键字来创立一个常量
//1、Dart 运用 const 关键字来界说一个常量
//2、Dart 可以运用 const 关键字替代 var 或加在类型前面
const a = 10;
const b = true;
const int aa = 10;
const bool bb = true;
//3、Dart 还可以运用 const 关键字来创立一个常量
//创立一个内容和引证都不行变的 list 数组
var list = const [1,2,3];
//创立一个内容和引证都不行变的 set 调集
var set = const {1,2,3};
这儿我有一个疑问:那 const 和 final 有啥异同呢?
答:
异:
1、final 可以一开始不赋值,假如赋值了则不行变。const 一开始就需求赋值且不行变
2、const 有必要给一个清晰的编译常量值(即编译期间就确定的值)
3、final 可以经过核算或许办法获取一个值(即运转期间确定的值)
4、final 表明引证不行变,但内容是可变的。const 表明内容和引证都不行变
同:
1、final,const 关键字都可以用来界说一个常量
//1、final 可以一开始不赋值,假如赋值了则不行变。const 一开始就需求赋值且不行变
//2、const 有必要给一个清晰的编译常量值(即编译期间就确定的值)
//3、final 可以经过核算/办法获取一个值(即运转期间确定的值)
final a;//编译经过
a = 10;//编译经过
const b;//编译报错
b = 10;//编译报错
//4、final 表明引证不行变,但内容是可变的。const 表明内容和引证都不行变
final set = {1,2,3};
set.add(4);
var list = const [1,2,3];
list.add(4);//运转报错,const list 不行新增元素
1.3、办法
1.3.1、办法界说
1)、办法和函数是同一个概念,在 Java 中咱们习气叫办法 (method)。在 Kotlin 中咱们习气叫函数 (function)。因 Dart 更像 Java ,因而这儿主张咱们也叫办法 (method)
2)、办法是运转代码的载体,像咱们运用过的 main 办法便是一个办法
Dart 中界说办法的语法规矩:
回来参数类型 办法名(参数1,参数2,参数3…) {
办法体
}
//1、Dart 写法1:办法的参数运用:var 参数名
String methodName(var name,var age){
return "erdai666";
}
//2、Dart 写法2:办法的参数运用:类型 参数名
String methodName(String name,int age){
return "erdai666";
}
//3、Dart 写法3:办法的回来类型可省掉,依据办法体最终一行代码进行回来类型揣度
methodName(var name,var age){
return "erdai666";
}
//4、假如没有写回来类型,且办法体最终一行代码没有清晰写回来句子,那么默许履行:return null
methodName(var name,var age){
}
//5、Dart 写法4:无回来值运用 void 关键字
void methodName(var name,var age){
}
//6、Dart 写法5:假如办法体只需一行表达式,可将其改成单行办法款式,办法名和办法体用 => 衔接
String methodName(var name,var age) => "erdai666";
办法语法解说:
-
一切办法都有回来值,即便回来值是 void
-
办法的回来类型,可写可不写。假如不写,会依据办法体里边最终一行代码进行类型揣度
-
假如没有写回来类型,且办法体最终一行代码没有清晰写回来句子,那么默许履行:return null
-
办法名称可以随意取,就像 Java ,Kotlin 里边界说办法名相同
-
办法名里边的参数可以有恣意多个,参数的声明格局有两种:
1、var 参数名
2、类型 参数名
- 假如办法体只需一行表达式,可将其改成单行办法款式,办法名和办法体用 => 衔接
小主张:界说一个办法时,主张把回来类型给写出来,可读性强
1.3.2、可选参数 & 命名参数 & 默许参数
可选参数
1)、可选参数望文生义便是可以挑选的参数,运用 [] 表明可选的方位参数,如下:
void optionFunction(var value1,[var value2 = 2,var value3 = 3]){
print('$value1 $value2 $value3');
}
void main(){
optionFunction(1);
}
//打印成果
1 2 3
疑问:假如我只想给 value1 和 value3 传参:可以做到吗?
答:不能。假如想做到,就需求运用命名参数
命名参数
1)、命名参数默许都为可选参数。假如是必要参数,则需求用 required 关键字,且运用 required 润饰的参数不能供给默许值
2)、运用 {} 来指定命名参数
3)、命名参数有必要以 key: value 的形式去指定
如下:
//1、运用 {} 来指定命名参数
void optionFunction(var value1,{var value2 = 2,var value3 = 3}){
print('$value1 $value2 $value3');
}
void main(){
optionFunction(1,value3: 4);
}
//打印成果
1 2 4
//2、假如是必要参数,则需求用 required 关键字,且运用 required 润饰的参数不能供给默许值
//此刻 value2 为必传的参数
void optionFunction(var value1,{required var value2,var value3 = 3}){
print('$value1 $value2 $value3');
}
void main(){
optionFunction(1,value2: 4);
}
//打印成果
1 4 3
默许参数
如上咱们方才给可选参数和命名参数供给的默许值
1)、默许参数便是给可选参数供给默许值,以便在未供给相应实参时运用
2)、默许值有必要是编译时常量
3)、假如可选参数没有供给默许值,那默许值就为 null
//1、假如可选参数没有供给默许值,那默许值就为 null
void optionFunction(var value1,{var value2 = 2,var value3}){
print('$value1 $value2 $value3');
}
void main(){
optionFunction(1);
}
//打印成果
1 2 null
1.3.3、匿名办法(又称闭包)
1)、匿名办法望文生义便是没有名字的办法,语法规矩如下:
//1、办法一:
(var 参数名){
办法体
}
//2、办法二:
(类型 参数名){
办法体
}
//3、办法三:假如办法体只需一行代码可以将匿名办法用单行表明
(var 参数名) => 办法体
(类型 参数名) => 办法体
//4、办法四:省掉 var 或类型
(参数名){
办法体
}
(参数名) => 办法体
2)、匿名办法一般会作为参数或赋值给一个变量
//1、匿名办法当作参数运用
void main(){
const list = [1,2,3];
list.forEach((element){
print(element);
});
//可简化成如下写法:
//list.forEach((element) => print(element));
}
//2、匿名办法赋值给一个变量
void main(){
const list = [1,2,3];
var function = (element){
print(element);
};
//可简化成如下写法:
//var function = (element) => print(element);
list.forEach(function);
}
3)、匿名办法当即履行
//1、办法1
void main(){
var func = (){
print('666');
};
(func)();
}
//2、办法2
void main(){
((){
print('666');
})();
}
//打印成果
666
4)、匿名办法内部可以引证包含该匿名办法的一切层级作用域中的变量,与匿名办法调用的方位无关,如下:
//makeAdder 回来一个匿名办法
Function makeAdder(num addBy){
//匿名办法拜访 addBy 参数
return (num i) => addBy + i;
}
void main(){
//创立一个匿名办法,传入实参为 2
var add1 = makeAdder(2);
//创立一个匿名办法,传入实参为 3
var add2 = makeAdder(3);
//别离调用两个匿名办法,并打印成果
print(add1(3));
print(add2(4));
}
//打印成果
5
7
留意:Dart 中的办法也是一种类型,对应 Function 类,所以办法可以被赋值给变量或作为参数传入另一个办法
1.3.4、静态办法
1)、运用 static 关键字润饰的办法即为静态办法,因静态办法不归于类实例,所以也无法拜访类成员
2)、静态办法可以运用类名直接调用
class Test{
static String staticFunction1(){
return "";
}
static void staticFunction2(){
}
}
void main(){
Test.staticFunction1();
Test.staticFunction2();
}
二、根本类型和运算符
2.1、根本类型
前面提到过:Dart 彻底扔掉了 Java 中的根本数据类型,全部都是目标数据类型。因而咱们这儿讲的根本类型,也是目标数据类型,只不过是 Dart 默许给咱们供给的
2.1.1、数字类型
1)、在dart言语中数字类型首要有下面三种:
int:整数类型
double:浮点数类型
num:数字类型,int和double都是它的子类
var x = 1; // 初始化为 int 类型
var y = 1.1; // 包含小数,初始化为 double
// 清晰指定数据类型
double z = 1;
num d = 100;
2)、数字类型和字符串类型相互转化
// 将 String 类型转化成 int 类型
var one = int.parse('1');
// 将 String 类型转化成 double 类型
var onePointOne = double.parse('1.1');
// 将 int 类型转化成 String 类型
String oneAsString = 1.toString();
// 将 double 类型转化成 String 类型, 保留两位小数
String piAsString = 3.14159.toStringAsFixed(2);
2.1.2、字符串类型
1)、字符串类型运用单引号或许双引号包裹字符串都可以
var s1 = 'Hello';
var s2 = "erdai";
2.1.2.1、字符串内嵌表达式
1)、Dart 支撑在字符串中内嵌变量,或许爽性内嵌表达式
// 内嵌变量
var s3 = '你好: $s2';
// 内嵌表达式
var s4 = "转大写:${s2.toUpperCase()}";
2.1.2.2、字符串相加(衔接)
字符串相加,便是将两个字符串衔接起来,dart 言语中有以下两种办法完结字符串衔接:
1)、接连的字面字符串界说,默许会将字符串衔接起来
2)、运用 + 加号衔接字符串
void main(){
//1、接连的字面字符串界说,默许会将字符串衔接起来
var str1 = "erdai" "666";
//2、运用 + 加号衔接字符串
var str2 = "erdai" + "666";
print(str1);
print(str2);
}
//打印成果
erdai666
erdai666
2.1.2.3、多行字符串界说
1)、运用 ”’ 三引号界说多行字符串,这种办法可以保留字符串的换行符
void main() {
var s1 = '''
这是榜首行字符串。
这是第二行字符串。
''';
print(s1);
}
//打印成果
这是榜首行字符串。
这是第二行字符串。
2.1.3、布尔类型
布尔类型就两种值:true 或许 false, 别离表明真和假
var isOk = false;
bool status = true;
2.1.4、枚举类型
1)、枚举类型其实便是一组常量的调集,都是只读的
// 运用 enum 关键字,界说 Color 枚举类型,Color 包含了3个常量 red、green、blue
enum Color { red, green, blue }
// 读取 Color.blue 枚举常量
var aColor = Color.blue;
// 运用 switch 句子判别 aColor 变量值
switch (aColor) {
case Color.red: // 运用枚举常量作为检测条件,aColor == Color.red 则建立。
print('Red as roses!');
break;
case Color.green:
print('Green as grass!');
break;
default: // 默许条件
print(aColor); // 'Color.blue'
}
2)、枚举常量都有一个从 0 开始数字编号,榜首个常量是 0,第二个是 1,以此类推
void main() {
print(Color.green.index);
print(Color.blue.index);
}
//打印成果
1
2
2.2、运算符
运算符这一块,除了级联调用是 Java 和 Kotlin 所没有的,其他运算符根本相似
2.2.1、级联调用
1)、级联调用便是经过 .. (两个接连的点) 接连调用目标的特点和办法
querySelector('#confirm') // 经过 querySelector 查询取得一个目标
..text = 'Confirm' // 设置目标 text 特点
..classes.add('important'); // 调用目标的 classes 特点的 add 函数
//等价如下代码
// 经过 querySelector 查询取得一个目标
var button = querySelector('#confirm');
// 设置目标 text 特点
button.text = 'Confirm';
// 调用目标的 classes 特点的 add 函数
button.classes.add('important');
留意:咱们先关注语法即可
2.2.2、赋值运算符
//1、运用 = 进行赋值
a = 100;
//2、复合赋值运算符
a *= 3; // 等价于 a = a * 3
a -= 3; // 等价于 a = a - 3
a += 3; // 等价于 a = a + 3
a /= 3; // 等价于 a = a / 3
a %= 3; // 等价于 a = a % 3
2.2.3、管用运算符
运算符 | 阐明 |
---|---|
+ | 加 |
– | 减 |
-expr | 管用取反 |
* | 乘 |
/ | 除 |
~/ | 除法,成果取整 |
% | 求余 |
++ | 支撑前置自增和后置自增 |
— | 支撑前置自减和后置自减 |
// a = 5
var a = 2 + 3;
// a1 = -1
var a1 = 2 - 3;
// a2 = 6
var a2 = 2 * 3;
// a3 = 2.5
var a3 = 5 / 2;
// 整除,a4 = 2
var a4 = 5 ~/ 2;
// 求余数 a5 = 1
var a5 = 5 % 2;
//a++ 和 ++a 差异:a++ 先用在加,++a先加在用
a++; // 相当于 a = a + 1
++a;
a--; // 相当于 a = a - 1
--a;
2.2.4、联系运算符
联系运运算符常用于条件表达式中,判别条件是否建立
运算符 | 阐明 |
---|---|
== | 判别两个值是否相等 |
!= | 判别两个值是否不相等 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
2.2.5、类型测验运算符
运算符 | 阐明 |
---|---|
as | 用于类型转化,将一个目标类型转化成其他一种目标类型,一般用于子类目标转化成父类目标。 |
is | 用于检测一个变量是否归于某种目标类型 |
is! | 用于检测一个变量不归于某种目标类型 |
// 假如 emp 变量是 Person 类型则条件为 true
if (emp is Person) {
// 忽略代码
}
// 永远回来 true, 由于一切类型都承继了 Object。
if (emp is Object) {
// 忽略代码
}
留意:假如变量是某个类的子类的实例,那么这个变量也归于父类类型,is 条件回来 true
2.2.6、逻辑运算符
运算符 | 阐明 |
---|---|
!expr | 表达式条件取反 |
|| | 逻辑或 |
&& | 逻辑与 |
//常用于条件句子组合表达式
if (!done && (col == 0 || col == 3)) {
//...
}
2.2.7、位运算符
二进制位运算符
运算符 | 阐明 |
---|---|
& | 与 |
| | 或 |
异或 | |
~expr | 按位取反 |
<< | 左移 |
>> | 右移 |
// a = 256
var a = 1 << 8; // 1 左移 8 位相当于:1 * 2 的 8 次方
2.2.8、条件运算符
Dart 中有两种条件运算符:
1、语法规矩:condition ? expr1 : expr2 ,相似 Java 三目运算符。condition 表达式为真,则履行并回来 expr1 的值, 不然履行 expr2
2、语法规矩:expr1 ?? expr2 ,相似 Kotlin 的 ?: 。假如 expr1 不等于 null, 则履行 expr1 并回来 expr1 的值,不然履行并回来 expr2 的值
// 假如isPublic为真,visibility = 'public' 不然 visibility = 'private'
var visibility = isPublic ? 'public' : 'private';
// 这种语法在处理参数默许值十分有用。
// 假如name不等于null, 则payerName = name 不然payerName = 'Guest'
String payerName = name ?? 'Guest';
三、数组和调集
3.1、List 数组
3.1.1、界说
1)、与 Java 的数组类型不同,Dart 中的数组类型便是 List,它是泛型类型数据结构,支撑恣意数据类型的数组
2)、List 数组界说的元素有序可重复,相似 Java 的 List 调集
3)、Dart 中 List 数组首要分为两种类型:
1、可变长度数组
2、固定长度数组
不管是哪种类型的数组,他们的操作办法是相同的
//1、可变长度数组
//界说一个 int 类型的可变数组
List<int> a = []; //等价:var a = <int>[];
//界说一个 String 类型的可变数组
var strs = <String>[]; //等价 List<String> strs = [];
//如下这种界说办法,编译器会给咱们揣度出是字符串类型的可变数组
var strs = ['字符串'];
1、了解 Java 的人都知道,上述这种界说便是泛型类型的语法,<> 符号界说的是 List 的元素类型
2、上述界说数组咱们并没有指定数组巨细,因而他们界说的都是可变数组,可变数组可以往数组中刺进不约束数量的元素 (只需没超过内存约束)
//2、固定长度数组
//界说一个固定长度为 3 ,类型为 int 的数组
var list = List.filled(3,0);
//测验一
void main() {
//界说一个固定长度为 3 ,类型为 int 的数组
var list = List.filled(3,0);
list[0] = 1;
list[1] = 2;
list[2] = 3;
//此刻假如咱们添加第 4 个元素,则会报数组下标越界反常
//list[3] = 4; //数组下标越界
for (var value in list) {
print(value);
}
}
//打印成果
1
2
3
//测验二:
void main() {
//界说一个固定长度为 2 ,类型为 String 的数组
var list = List.filled(2,"");
list[0] = "erdai";
list[1] = "666";
for (var value in list) {
print(value);
}
}
//打印成果
erdai
666
留意:
1、上述 filled 办法的两个参数:榜首个表明数组长度,第二个表明存放的元素类型初始值
2、固定长度的数组,只能经过数组下标的办法读写数组,不能运用 add,insert 办法修正数组,不然会报错
3.1.2、扩展运算符
假如咱们想将一个 List 数组的元素填充到其他一个数组去,咱们可以运用扩展运算符 … ,如下:
var list = [1, 2, 3];
var list2 = [0, ...list]; // 将 list 数组的一切元素一个个展开来,刺进到 list2 中
// 等价代码 var list2 = [0, 1, 2, 3]
3.1.3、常用 Api 介绍
//1、首要界说一个 int 类型的可变数组
var a = <int>[];
//2、往数组尾巴添加元素 a:[1,2,3]
a.add(1);
a.add(2);
a.add(3);
//3、修正榜首个元素的值 a:[0,2,3]
a[0] = 0;
//4、在数组 0 方位,刺进 100 a:[100,0,2,3]
a.insert(0,100);
//5、删去一个元素 a:[100,0,2]
//依据元素删去
a.remove(3);
//依据下标删去 a:[100,2]
a.removeAt(1);
//6、获取数组巨细
print(a.length); //打印:2
//7、数组排序:默许数组从小到大排序 a:[2,100]
a.sort();
//8、判别数组是否包含指定元素
a.contains(2); // true
//9、清空 List,删去一切数据 a:[]
a.clear();
3.2、Set 调集
3.2.1、界说
1)、Dart 中的 Set 是无序调集类型,Set 跟 List 都能保存一组数据,差异便是 Set 的元素都是仅有的,和 Java 的 Set 调集相似
2)、Set 支撑恣意类型数据,首要有下面三种办法初始化:
//1、办法一:运用 {} 界说一个 String 类型的 Set
var strSet = {"str"};
//2、办法二:界说一个空的 String 类型的 Set
var names = <String>{};
//3、办法三:经过 Set 目标界说一个可以保存 String 类型的 Set
var names = Set<String>();
3.2.2、常用 Api 介绍
//1、首要界说一个 set 调集
var names = <String>{};
//2、添加一个元素 names:{"Dart"}
names.add("Dart");
//3、添加一个 List 数组 names:{"Dart","Flutter"}
var titles = ["Flutter"];
names.addAll(titles);
//4、获取 Set 巨细
print(names.length); //打印:2
//5、删去元素
//依据元素进行删去 names:{"Dart"}
names.remove("Flutter");
//6、判别 Set 是否包含指定元素
names.contains("Dart"); //true
//7、清空 Set 一切元素 names:{}
names.clear();
3.3、Map 调集
3.3.1、界说
1)、Dart 中 map 类型,便是一种哈希类型数据,map 类型的数据都是由 key 和 value 两个值组成,key 是仅有的,value 不必仅有,读写数据都是经过 key 进行,map 也是泛型类型,支撑恣意类型数据,key 和 value 可以是恣意类型数据
2)、map 首要有以下四种办法初始化:
//1、办法一:直接以key, value 的办法初始化一个 map 类型变量, key 和 value 都是 String 类型
var map1 = {
//格局 Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};
//2、办法二:直接经过 Map 类结构一个 map 类型变量, key 类型为 String, value 类型为 int
var map2 = Map<String, int>();
//3、办法三:界说一个 key 类型为 String, value 类型为 int 的空 Map
var map3 = <String,int>{}
//4、办法四:界说一个绕过编译查看的动态 key,value 类型 Map
var map4 = Map(); //等价于:var map4 = {};
3.3.2、常用 Api 介绍
//1、首要界说一个 map
var map = {};
//2、向 map 添加数据 map:{"key1":"value1","key2":"value2"}
//运用 [] 操作符读写 map 数据,语法:map变量[key]
map["key1"] = "value1";
map["key2"] = "value2";
//3、查询 map:运用[]操作符,依据 key 查询对应的 value 值,假如 key 不存在则回来 null
var v1 = map["key1"];
var v2 = map["key2"];
//4、更新 map 数据:依据 key 更新 map 数据和添加 map 数据用法相同
//假如 key 值不存在则添加,不然更新 key 的数据
//map:{"key1":"value1","key2":"value2"}
map["key2"] = "erdai";
//5、获取 map 巨细
print(map.length); //打印:2
//6、遍历 map
map.forEach((k, v) {
print('$k $v');
});
//7、删去 map 数据:经过 remove 函数可以删去指定的 key 数据
//map:{"key2":"value2"}
map.remove("key1");
//8、清空一切 map 数据 map:{}
map.clear();
四、程序的逻辑操控
这个章节相对简略,咱们就简略举个比如
4.1、if-else
if (isRaining()) {
// 代码1
} else if (isSnowing()) {
// 代码2
} else {
// 代码3
}
else是可选的,依据需求组合即可
4.2、for-i 和 for-in 循环
// 界说 int 数组
var list = [5,1,2,6,3];
//1、for-i 循环遍历数组
for (var i = 0; i < list.length; i++) {
// 打印数组元素
print(list[i]);
}
//2、for-in 循环遍历数组变量 list
for (var v in list) {
// 打印数组元素 v
print(v);
}
//打印成果
5
1
2
6
3
4.3、switch句子
1)、switch 句子的作用跟 if 句子相似,用于检测各种条件是否建立,然后履行相应分支的代码
2)、switch 支撑检测 int,String 类型变量的检测,当然假如你自界说的类重载了 == 操作符,也可以在 switch 条件中运用
// 条件状况变量
var command = 'OPEN';
switch (command) { // 需求检测的变量
case 'CLOSED': //case 句子用于设置检测条件
executeClosed(); // 假如 command = 'CLOSED',履行当时分支代码。
break; // 完毕当时分支履行逻辑
case 'PENDING':
executePending();
break;
case 'APPROVED':
executeApproved();
break;
default: // 假如上面的 case 句子都没有匹配成功,则履行 default 分支的逻辑。
executeUnknown();
}
4.4、while 和 do-while 循环句子
//1、while 循环句子比如
void main() {
// 界说 int 数组
var list = [5, 1, 2, 6, 3];
// 循环遍历数组
var i = 0;
while (i < list.length) {
// 条件为 true 则履行循环体代码
print(list[i]); // 打印数组元素
i++; // 数组下标递加
}
}
//2、do-while 循环句子比如
void main() {
// 界说 int 数组
var list = [5, 1, 2, 6, 3];
// 循环遍历数组
var i = 0;
do {
print(list[i]);
i++;
} while (i < list.length); // 先履行循环体代码后再检测循环条件,条件为 true 则持续履行循环
}
//打印成果
5
1
2
6
3
五、面向目标编程
Dart 是面向目标编程言语,目标都是由类创立的,一切类都是由 Object 类派生出来的子类,除了 Object , 一切类只需一个父类(即只能承继一个父类)
尽管 Dart 言语中一个类只能承继一个父类,可是 Dart 言语供给了 mixin 机制,可以复用多个类,达到相似多承继的作用
5.1、类和目标
1)、Dart 没有 public、protected 和 private 等成员拜访限定符。默许状况下特点,办法,类等都是共有的,相似 Java 的 public。假如想要表明私有,则以下划线 _ 最初去命名
2)、Dart 中实例化目标和 Java 相似,new 关键字可写可不写
3)、当咱们在类中创立私有特点时,咱们应该给私有特点供给 getter 和 setter 办法供外界拜访:
get 办法语法格局:回来值类型 get 办法名 { 办法体 }
set 办法语法格局:set 办法名 ( 参数 ) { 办法体 }
class Person {
// 界说类成员特点,默许类的成员特点和办法都是共有的,相似 java 的 public
var name;
// 以下划线 ( _ ) 最初命名的特点代表私有成员特点
var _age;
// 跟类名同名的办法,为结构办法
// 这儿自界说了一个带着参数的结构办法。
// 假如咱们没有自界说结构办法,会主动生成一个不带参数的默许结构办法
Person(var name, var age) {
// 由于参数名和类特点名同名,可以运用this引证当时目标
this.name = name;
// 可以忽略this关键字,直接引证类成员
_age = age;
}
//为 _age 供给 getter 和 setter 办法
int get age{
return _age;
}
//getter 办法还可以简化为此写法:int get age => _age;
set age(int age){
_age = age;
}
// 定一个 public 的办法
String greet(String who) => 'Hello, $who. I am $name, my age is $_age !';
}
void main(){
var person = Person("erdai",18);
//下面这句便是调用了 age 的 set 办法
person.age = 20;
var greet = person.greet("lucy");
print(greet);
}
//打印成果
Hello, lucy. I am erdai, my age is 20 !
5.2、结构办法
假如咱们没有自界说一个结构办法,会主动生成一个不带参数的默许结构办法
// 这个类会生成默许的结构办法
class Person {
String name;
}
// 经过默许结构办法实例化目标
var p = Person();
5.2.1、自界说结构办法
class Point{
var x,y;
Point(var x,var y){
// 经过this拜访成员特点,当然一般除非出现命名抵触,不然可以忽略this
this.x = x;
this.y = y;
}
}
关于结构办法中,简略的赋值操作,Dart言语供给了更简练的语法,如下:
class Point{
var x,y;
// 直接将结构办法的榜首个参数赋值给this.x, 第二个参数赋值给this.y
Point(this.x,this.y);
}
5.2.2、初始化参数列表
Dart 还为结构办法供给了 参数初始化列表 的语法,用于初始化目标参数
class Point{
var x,y;
// 冒号 : 后边的表达式便是参数初始化列表,每个表达式用逗号分隔
Point(var x,var y): this.x = x,this.y = y{
// 运用参数初始化列表初始化目标特点,这儿假如没有其他初始化工作要做,可以是空的
}
}
5.2.3、命名结构办法
1)、Dart 可以运用命名结构办法语法,创立多个结构办法,命名结构办法语法格局: 类名.结构办法名(参数列表)
class Point{
var x,y;
Point(this.x,this.y);
// 命名结构办法 namedConstructor
Point.namedConstructor(){
x = 0;
y = 0;
}
}
void main(){
// 运用命名结构办法实例化目标
var point = Point.namedConstructor();
}
上面的比如也可以改写为:
class Point{
var x,y;
Point(this.x,this.y);
// 命名结构办法 namedConstructor
// 这儿运用参数初始化列表,直接经过 this 调用上面的结构办法,传入两个参数 0,初始化目标
Point.namedConstructor():this(0,0);
}
5.2.4、factory 结构办法
1)、Dart 供给了一个特别的结构办法,相似设计形式中的工厂形式,用来创立目标
2)、factory 结构办法只能拜访静态特点和静态成员办法,因而不能拜访 this 引证
//1、界说个日志类
class Logger {
final String name;
bool mute = false;
// 界说一个私有的_cache特点,用来保存创立好的Logger目标
static final Map<String, Logger> _cache = {};
// 留意这个结构办法,前面运用了factory关键字润饰,这代表这个结构办法是一个工厂结构办法
// 工厂结构办法不会每次都创立一个新的Logger目标
factory Logger(String name) {
// 依据name判别缓存的Logger目标是否存在
if (_cache.containsKey(name)) {
// 回来缓存的Logger目标
return _cache[name]!;
} else {
// 假如没有缓存,则调用命名结构办法_internal创立一个Logger目标
final logger = Logger._internal(name);
// 依据name缓存logger
_cache[name] = logger;
// 回来新的Logger目标
return logger;
}
}
// 留意这个是一个私有的命名结构办法。
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
//2、测验
void main(){
var logger = Logger("erdai");
logger.log(logger.name);
}
//打印成果
erdai
5.3、承继和多态
5.3.1、承继
1)、Dart 经过 extend 关键字承继一个类,和 Java 相似
2)、子类会承继父类可见的特点和办法,不会承继结构办法
3)、子类可以复写父类的 getter,setter,以及一般办法,运用 @override 表明覆写
class Parent{
String name = "";
int age = 0;
//核算特点
bool get adult => this.age > 18;
//私有特点,关于子类不行见
String _address = "";
void method(){
print('Parent');
}
}
class Children extends Parent{
void specificMethod(){
print('Children specificMethod');
}
}
void main(){
var child = Children();
//调用子类自己的办法
child.specificMethod();
//拜访父类的特点
child.name = "erdai";
child.age = 18;
print('${child.name} ${child.age}');
//调用父类的办法
child.method();
//拜访父类的核算特点
print('${child.adult}');
}
//打印成果
Children specificMethod
erdai 18
Parent
false
5.3.2、多态
1)、简略的了解:多态便是将子类的目标赋值给父类的引证,同一个办法调用会有不同的履行作用
2)、多态的体现:父类界说一个办法,让承继它的子类去完结,每个子类有不同的表现
class Animal{
void animalType(){
}
}
class Dog extends Animal{
@override
void animalType() {
print('I am dog');
}
}
class Pig extends Animal{
@override
void animalType() {
print('I am pig');
}
}
void main(){
//子类的目标赋值给父类的引证
Animal animal1 = Dog();
Animal animal2 = Pig();
//同一个办法调用会有不同的履行作用
animal1.animalType();
animal2.animalType();
}
//打印成果
I am dog
I am pig
5.4、笼统类和笼统办法
1)、笼统类便是不能实例化的类,经过 abstract 关键字声明
2)、笼统办法便是没有完结的办法,Dart 中的笼统办法不能用 abstract 声明,Dart 中没有办法体的办法就称为笼统办法
3)、承继笼统类,子类有必要要完结一切笼统办法,不然会报错
// 运用 abstract 关键字润饰的类,便是笼统类
abstract class Doer{
// 笼统类跟一般类相同,可以界说成员变量,成员办法。
String name = "";
// 界说个笼统办法,这个办法咱们没有完结详细的功用
void doSomething();
}
// 承继笼统类 Doer
class EffectiveDoer extends Doer{
// 完结笼统类的笼统办法
@override
void doSomething() {
print('doSomething');
}
}
void main(){
var doer = EffectiveDoer();
doer.doSomething();
doer.name = "erdai";
print(doer.name);
}
//打印成果
doSomething
erdai
5.5、接口
1)、Dart 中的接口没有运用 interface 关键字界说,而是一般类和笼统类都可以作为接口被完结。可是一般都是用笼统类来界说接口
2)、子类经过 implements 来完结接口
3)、默许状况每一个类都隐含一个包含一切公有成员(特点和办法)的接口界说
abstract class Fruit{
// 包含在隐式接口里边
String name = "";
// 结构办法不包含在隐式接口里边
Fruit(this.name);
// 包含在隐式接口里边
void eat();
}
class Apple implements Fruit{
@override
String name = "苹果";
@override
void eat() {
print('吃$name');
}
}
void main(){
var fruit = Apple();
fruit.eat();
}
//打印成果
吃苹果
留意:尽管一般类也可以作为接口完结,可是仍然需求完结一般类里边一切的公有成员(特点和办法),因而主张咱们运用笼统类来作为接口完结,由于笼统类原本便是用来界说给子类完结的
六、空安全查看
1)、Dart 在 2.12 版别和 Flutter 2.0 中引入了空安全的新特性,在空安全版别下,运转时的 NPE (NullPointer Exception) 反常被提前到了编译期
2)、在空安全推出之前,静态类型体系答应一切的类型值为 null,由于 Null 是一切类型的子类。而在空安全推出后,一切类型默许为不行空类型,Null 不再是一切类的子类,它变成了和其他类型并行的类
3)、Dart 新增了一些关键字用于空安全,如下:
关键字 | 意义 | 示例 |
---|---|---|
? | 可空 | int a?; |
! | 非空 | int b = a!; |
late | 延迟初始化 | late int a; |
required | 可选参数的不行空 | {required int a} |
6.1、空类型声明符 ?
1)、在类型后边加上 ?,表明可空类型
2)、运用 var 关键字界说的变量也是可空类型
3)、可空类型变量的调用,运用 ?. 操作符,它表明假如当时目标不为 null 则调用,为 null 则什么都不做
//1、在类型后边加上 ?,表明可空类型
void main() {
//界说一个 String 的可空类型,默许值为 null
String? str;
//编译报红,由于可空类型需求运用 ?. 调用
//提示:The property 'length' can't be unconditionally accessed because the receiver can be 'null'.
print(str.length);
}
//2、运用 var 关键字界说的变量也是可空类型
void main() {
//界说一个动态的可空类型 str,默许值为 null
var str;
//下面这句代码会绕过编译器查看,但运转时会报错:NoSuchMethodError: 'length'
print(str.length);
}
//3、可空类型变量的调用,运用 ?. 操作符,它表明假如当时目标不为 null 则调用,为 null 则什么都不做
void main() {
String? str1;
var str2;
print(str1?.length);
print(str2?.length);
}
//打印成果
null
null
6.2、非空断语 !
1)、运用 ! 关键字表明告诉编译器这是一个不行能为空的变量。假如为空,你就抛反常
String? getName() => "erdai";
void main() {
String? str = getName();
//此刻会编译报红,由于编译器无法智能判空
//print(str.length);
//因而咱们需求运用 ! 关键字
print(str!.length);
}
//打印成果
5
6.3、late 延迟初始化
1)、late 关键字会告诉编译器:这是个非空变量,我稍后会初始化
//此刻会编译报红,由于编译器会告诉咱们非空变量有必要先初始化
//String str;
//因而咱们需求运用 late 关键字
late String str;
void main() {
str = "erdai";
print(str);
}
//打印成果
erdai
6.4、required 关键字
1)、required 关键字首要是用来符号命名参数,在运用时一定要给他们赋值,使得他们不为空
2)、运用 required 润饰的参数不能供给默许值
void optionFunction(var value1,{required var value2,var value3 = 3}){
print('$value1 $value2 $value3');
}
void main() {
optionFunction(1, value2: 100);
}
//打印成果
1 100 3
七、有趣的运算符重载
与 Kotlin 相似,Dart 的运算符重载答应咱们让恣意两个目标进行相加,或许是进行其他更多的运算操作
1)、运算符重载运用的是 operator 关键字,咱们只需求在指定运算符前面加上 operator 关键字,就可以完结运算符重载的功用了,Dart 支撑的重载运算符如下:
< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
– % >>
2)、重载运算符的语法格局如下:
函数回来值 operator 运算符(运算符参数) {
// 完结运算符重载逻辑
}
下面咱们就来实践一下:
class Money{
int value = 0;
Money(this.value);
//重载 + 号运算符, 两个 Money 目标相加,然后回来一个新的 Money 目标
Money operator +(Money money){
var sum = value + money.value;
return Money(sum);
}
}
void main() {
var money1 = Money(100);
var money2 = Money(200);
//两个目标相加
var money3 = money1 + money2;
print(money3.value);
}
八、 mixin 混入
1)、前面提到 Dart 言语的类是单承继的,假如咱们想要完结相似多承继的作用可以运用 mixin 机制,又叫混入机制,例如把类 A 混入到类 B 中,那么类 B 就具有了类 A 的成员,跟承继的特性十分相似
2)、界说一个可以被 mixin 的类,运用 mixin 关键字替代 class 关键字即可
3)、承继被 mixin 的类,运用 with 关键字,假如有多个,中心用 , 隔开
4)、被 mixin 的类只能承继自 Object,不能承继其他类,且不能有结构办法
5)、父类束缚:当声明一个 mixin 时, on 后边的类便是这个 mixin 的父类束缚。一个类若是要 with 这个 mixin,则这个类有必要承继或完结这个 mixin 的父类束缚
6)、就远射中准则:当 with 多个 mixin,多个 mixin 具有同一个办法,则调用办法时会射中最终一个 mixin 类的办法
//1、界说一个可以被 mixin 的类,运用 mixin 关键字替代 class 关键字即可
//2、承继被 mixin 的类,运用 with 关键字,假如有多个,中心用 , 隔开
mixin A{
void getA(){
print('A');
}
}
mixin B{
void getB(){
print('B');
}
}
class C{
void getC(){
print('C');
}
}
class CC extends C with A,B{}
void main() {
var cc = CC();
cc.getA();
cc.getB();
cc.getC();
print(cc is A);
print(cc is B);
print(cc is C);
}
//打印成果
A
B
C
true
true
true
//3、被 mixin 的类只能承继自 Object,不能承继其他类,且不能有结构办法
class D {}
//编译报错,mixin 类不能承继其他类,只能承继自 Object
mixin E extends D{
//编译报错,mixin 类不能有结构办法
E();
}
//4、父类束缚:当声明一个 mixin 时, on 后边的类便是这个 mixin 的父类束缚。一个类若是要 with 这个 mixin,则这个类有必要承继
//或完结这个 mixin 的父类束缚
class F{}
mixin G on F{}
//class I with G{} //编译报错:class I 没有承继 mixin 的父类束缚
class I extends F with G{} //编译经过,class I 承继了 mixin 的父类束缚
//5、就远射中准则:当 with 多个 mixin,多个 mixin 具有同一个办法,则调用办法时会射中最终一个 mixin 类的办法
mixin Test1{
void testMethod(){
print('Test1 testMethod');
}
}
mixin Test2{
void testMethod(){
print('Test2 testMethod');
}
}
class Test with Test1,Test2{
}
void main() {
var test = Test();
test.testMethod();
}
//打印成果
Test2 testMethod
九、Dart 泛型
泛型编程机制最首要的意图是为了代码复用,避免类型转化反常。假如你对 Java ,Kotlin 泛型很了解,你会觉得 Dart 泛型十分简略。对 Java ,Kotlin 泛型还不了解的,看我这篇文章传送门
1)、Dart 中泛型首要有以下四种运用:
1、泛型类
2、泛型接口
3、泛型办法
4、约束泛型类型
9.1、泛型类,泛型接口,泛型办法
1)、咱们界说一个类,或许接口的时分,在类名后边添加泛型参数,便是为这个类或接口添加了一个泛型
2)、咱们界说一个办法时,在办法名后边添加泛型参数,便是为这个办法添加了一个泛型
3)、泛型语法格局:<T>
,多个泛型之间用 , 隔开:<T,K>
4)、泛型参数的命名可以随意取,可是咱们一般习气运用大写字母代表泛型参数
//一、泛型类
//1、界说泛型类
class GenericClass<T>{
T? name;
GenericClass(this.name);
void setName(T? value){
name = value;
}
T? getName(){
return name;
}
}
//2、泛型类运用
void main() {
var genericClass = GenericClass<String>("");
genericClass.setName("erdai");
print(genericClass.getName());
}
//3、打印成果
erdai
//二、泛型接口
//1、界说泛型接口
abstract class GenericInterface<K,V>{
void setKeyValue(K key,V value);
}
//2、界说泛型接口完结类
class GenericInterfaceImpl<K,V> implements GenericInterface<K,V>{
var map = {};
@override
void setKeyValue(K key, V value) {
map[key] = value;
}
}
//3、泛型接口运用
void main() {
var impl = GenericInterfaceImpl<String,int>();
impl.setKeyValue("erdai", 666);
impl.map.forEach((key, value) {
print('$key $value');
});
}
//4、打印成果
erdai 666
//三、泛型办法:类比 Java,Kotlin 中的写法
//Java 中的写法
public <T> void genericMethod(T param){
}
//Kotlin 中的写法
fun <T> genericMethod(param: T){
}
//Dart 中的写法
void genericMethod<T>(T param){
}
上述界说泛型类,泛型接口和 Java,Kotlin 没啥差异,却是界说泛型办法,咱们需求留意:
1、Java 中办法的泛型界说在回来值的前面
2、Kotlin 中的办法泛型界说在办法名的前面
3、Dart 中的泛型界说在办法名的后边
9.2、约束泛型类型
1)、约束泛型参数类型语法格局:<泛型参数 extends 父类>
class BaseClass{
void baseMethod(){
print('BaseClass baseMethod...');
}
}
class Child extends BaseClass{
@override
void baseMethod() {
print('Child baseMethod');
}
}
//T 类型有必要是 BaseClass 或许其子类
class Foo<T extends BaseClass>{
T? t;
Foo(this.t);
void fooTest(){
t?.baseMethod();
}
}
void main(){
//1、运用父类 BaseClass 作为泛型参数是答应的
var baseClass = BaseClass();
var foo1 = Foo<BaseClass>(baseClass);
foo1.fooTest();
//2、运用子类 Child 作为泛型参数
var childClass = Child();
var foo2 = Foo<Child>(childClass);
foo2.fooTest();
//3、假如不传入任何泛型参数,默许运用父类 BaseClass 作为泛型参数
var foo3 = Foo(baseClass);
foo3.fooTest();
}
//打印成果
BaseClass baseMethod...
Child baseMethod
BaseClass baseMethod...
十、Dart Import 导入包
在日常开发中,咱们常常需求导入咱们的本地模块或许第三方开源包。Dart 中首要经过 import 指令导入包
10.1、导入内置包
1)、Dart 内置了一些常用的包,这些内置的包会随着 Dart sdk 一同安装在本地
2)、导入内置包运用 dart: 作为途径前缀
// 导入内置 math 包,运用 dart: 作为前缀。
// math 包首要供给一些数学相关的函数,例如,正弦函数、求最大值函数等等
import 'dart:math';
void main() {
// 调用 math 包中的 max 函数,求两个数中的最大值。
var a = max(1,100);
print(a); //打印 100
}
10.2、包的别号
默许状况调用包中的函数或许类,不需求包名作为前缀,上面调用了 math 包中的 max 函数,直接运用包中的函数名。可是这样会存在命名抵触的可能性,假如导入的两个包,包含了同名的类或许函数,就会出现命名抵触,因而供给别号机制
1)、运用 as 关键字指定包的别号
//运用 as 关键字,指定包的别号
import 'dart:math' as math;
void main() {
// 运用别号,引证包中的函数 。
var a = math.max(1,100);
print(a); //打印 100
}
10.3、导入包的部分内容
1)、有时分咱们不想导入整个包,只想导入包里边的某个类或许某个函数。Dart 供给了show 和 hide 关键字处理导入包的部分内容
//1、仅导入 max 函数, 导入多个内容运用逗号分隔,例如 show max,sin
import 'dart:math' show max;
//2、除了max函数,导入 math 中的一切内容。
import 'dart:math' hide max;
10.4、导入本地模块
在日常开发中,咱们会常常会导入本地的模块,一般项目中会有多个 dart 脚本文件,每个 dart 脚本完结不同模块的代码,在需求的时分直接导入 dart 脚本文件即可
//例如我有个本地模块:libs/stack.dart
//导入本地模块
//直接经过本地文件途径导入 dart 脚本即可
import 'libs/stack.dart';
10.5、导入第三方开源包
10.5.1、查找第三方开源包
pub.dev/ :这个是 pub 的中央仓库, 上面有很多的第三方开源包,可以到这儿找到自己想要的包
10.5.2、装备依靠包
在项目根目录 pubspec.yaml 中装备 dependencies 特点,结构如下:
dependencies:
包名: 版别号
dependencies:
http: ^0.13.5
cupertino_icons: ^1.0.2
关于版别号阐明,如下:
^1.2.1 代表的更新版别规模为 >=1.2.1 && < 2.0.0
^0.2.1 代表的更新版别规模为 >=0.2.1 && < 0.3.0
^0.0.2 代表的更新版别规模为 0.0.2(相当于确定为了 0.0.2 版别)
规律: 实则便是把 ^ 后边非 0 的数字 +1 ,然后把其他位变为 0 便是它的最大版别。其他假如最终一位非 0 ,其他位为 0 ,就相当于锁版别。如:
最大版别:^1.2.1 => 2.2.1 => 2.0.0 规模:1.2.1-2.0.0
最大版别:^0.2.1 => 0.3.1 => 0.3.0 规模:0.2.1-0.3.0
^0.0.2:固定版别:0.0.2
10.5.3、下载依靠包
翻开命令行,输入如下命令:
flutter pub get
或许直接运用开发工具的可视化界面操作
10.5.4、导入第三方开源包
依靠包下载安装后,咱们就可以运用 import 导入第三方包,第三方包前缀为 package:
// 这儿导入 http 包,别号为 http
import 'package:http/http.dart' as http;
十一、Dart 反常处理
相似 Java,Dart 供给了 Exception 和 Error 两种类型的反常以及一些子类
1)、运用 throw 关键字抛出自界说类型反常,也可以将任何非 null 目标作为反常抛出
throw Exception('这是一个反常');
throw '这是一个反常';
小主张:一般主张抛出 Exception 和 Error , 或许他们的子类
2)、运用 try/on catch 配合捕获反常
void main() {
try {
var s;
print(s.length);
} on NoSuchMethodError catch (e) {
//捕获反常并打印
print(e);
} catch (e, s) {
//兜底处理 e:抛出的反常目标 s:栈信息,此参数可写可不写
print(e);
print(s);
}
}
上述代码:
1、运用 on 和 catch 来捕获反常:on 用来指定反常的类型,catch 则用来捕获目标
2、当抛出的错误并不是 on 指定的反常类型时,则走最终面的 catch 兜底
3、兜底 catch 办法有两个参数,榜首个参数是抛出的反常目标,第二个参数是栈信息
3)、运用 rethrow 再次抛出反常
void exceptionMethod(){
try {
dynamic b = true;
print(b++); //NoSuchMethodError
} catch (e) {
rethrow; //将上述反常再次抛出
}
}
void main() {
try {
exceptionMethod();
} catch (e) {
//捕获反常并打印
print(e);
}
}
十二、Dart 异步处理
Dart 是单线程模型的言语,假如咱们在程序中做耗时操作:恳求 Api 接口,文件 IO 等,就可能导致点击事情没有响应,程序卡顿之类的状况。为了处理这种状况,Dart 引入了异步操作机制:
1、Dart 异步处理不会堵塞线程,其他使命可以持续运转
2、由于 Dart 的异步机制并不涉及线程的切换,仅仅是由咱们的编程言语去操控,所以它的履行功率十分高
12.1、Dart 异步处理的用法
1)、Dart 言语中,有很多库的函数回来 Future 或许 Stream 目标,这些目标都是 Dart 对异步编程支撑的完结
Future – 代表一个异步核算使命,可以获取使命的核算成果
Stream – 代表一个异步的数据序列,一般用于读取接连的数据或许事情
12.1.1、Future
1)、Future代表的是一个异步的核算使命,假如使命还没履行完结,咱们是拿不到异步使命的成果
import 'package:http/http.dart' as http;
void main() {
var url = "https://www.baidu.com/";
//调用 get 函数恳求 url, 回来一个封装了 http 恳求使命的 future 目标
Future fTask = http.get(Uri.parse(url));
//打印 future 目标
print(fTask);
// 向 future 目标注册回调函数,处理恳求成果
fTask.then((response) => {
print('Response status: ${response.statusCode}')
});
// 打印 main 函数完毕符号
print('main end...');
}
//打印成果
Instance of 'Future<Response>'
main end...
Response status: 200
Process finished with exit code 0
上述代码:
1、首要打印了 fTask ,输出表明 fTask 是一个 Future 目标,将来会回来一个叫 Response 的成果目标
2、接下来打印了 main end… ,而不是先输出 http 的恳求状况码
3、最终打印了 http 的恳求状况码:Response status: 200 ,然后进程也退出了
上面这段程序在打印了 main end… 时进程并没有退出,而是比及打印了 http 的恳求状况码:Response status: 200 才退出,这也验证了咱们前面一个观念: Dart 的异步机制并不涉及线程的切换,仅仅是由咱们的编程言语去操控,所以它的履行功率十分高
12.1.2、await 和 async
上述这个比如存在一个问题:
1、需求注册回调函数,假如我有多层回调,可读性就会变得很差
此刻咱们可以运用 await 和 async 机制来处理这个问题,而且它还能让咱们运用同步的办法写出异步的代码
void main() async{
var url = "https://www.baidu.com/";
//恳求 url, 经过 await,等候 future 异步核算使命的成果,履行成功就直接回来成果
var response = await http.get(Uri.parse(url));
print('Response status: ${response.statusCode}');
print('main end...');
}
//打印成果
Response status: 200
main end...
上述代码:
1、输出成果的次序,跟咱们书写代码的次序共同
2、经过符号 async 和 await 关键字,咱们的异步代码,看起来跟同步代码没什么差异:
1、async 关键字的作用便是符号一个函数是异步函数
2、await 关键字的作用是等候异步使命的成果
留意: await 关键字只能在符号了async 的异步函数中运用,不然会报错
12.1.3、Stream
1)、Stream 代表一个异步的数据序列,是一种异步读取流式数据的办法,运用格局如下:
await for (数据类型 变量 in stream类型变量) {
// 处理数据
}
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for (final value in stream) {
sum += value;
}
return sum;
}
//async* 表明这是一个需回来 Stream 类型参数的异步函数
Stream<int> countStream(int to) async* {
for (int i = 1; i <= to; i++) {
//yield 在这儿表明暂时让出资源让其他代码履行
yield i;
}
}
void main() async{
var stream = countStream(10);
//经过 await 等候 sumStream 核算回来成果
var sum = await sumStream(stream);
print(sum);
}
//打印成果
55
上述代码咱们运用 await 符号 for in 循环句子,循环读取 stream 类型变量中的数据,代码书写也很直观,跟同步代码的书写办法共同
十三、Dart Isolate 并发
咱们知道一般常用的并发机制首要包含进程,线程以及后边的协程。可是 Dart 不一般 ,Dart 中的并发机制首要是由 Isolate 去完结的。所谓 Isolate ,你可以简略的了解是一种特别的线程
Isolate 的特点:
1、Isolate 之间不能同享内存
2、Isolate 之间只能经过音讯通讯
不能同享内存,意味着你不能像线程那样经过变量同享状况,每个 Isolate 都有自己独立的内存,这样设计的好处便是你不用加锁,也能安全的操作自己的数据
这儿你是否会有一个疑问️:前面咱们经过 Dart 异步机制处理了接口恳求之类的异步使命,不是也有相似并发的作用吗?那为什么还要引入 Isolate 并发机制呢?
答:前面咱们讲的异步机制你可以了解为一种假异步,由于它实际仍是在一个线程中去处理各种网络 IO,这些网络 IO 并不怎么消耗 CPU 资源,只是需求很多的等候恳求响应的时刻,因而咱们可以运用等候的闲暇时刻去处理其他使命,这便是异步机制可以进步功能的原因。这种机制其实和 Android Handler 机制有点相似。而现在假如你有一个核算量十分大的使命,例如:你需求对视频进行格局化处理,这个时分这些 CPU 密集型核算就会堵塞你的线程,导致其他使命都履行不了。因而针对这种比较耗 CPU 资源的使命,最好创立一个 Isolate 去处理,避免堵塞主 Isolate (也便是主线程),这样也可以运用设备的多核特性
13.1、Isolate 根本用法
// 导入 isolate 包
import 'dart:isolate';
void main() {
// 经过 Isolate.spawn 静态函数,创立一个新的 Isolate
// spawn 是一个泛型函数,承受一个泛型参数,表明 Isolate 进口函数承受的参数类型
// 这儿 spawn 的泛型参数是 String,subTask 是进口函数
// 第二个参数跟泛型参数类型共同,表明传递给进口函数的参数,这儿传入的是字符串
Isolate.spawn<String>(subTask, "my task");
print("main func end.");
}
// Isolate 进口函数界说,承受一个 String 参数
// 进口函数的参数类型由上面的 spawn 的泛型参数决定
void subTask(String msg){
print("subTask receive: $msg ");
}
//打印成果
main func end.
subTask receive: my task
经过输出,咱们发现先打印了 main func end,然后,履行新建 Isolate 的进口函数。 假如咱们想让代码履行次序,跟咱们书写次序共同的话,可以运用 await 关键字等候 Isolate 履行完毕:
// 导入 isolate 包
import 'dart:isolate';
// 运用 async 关键字将 main 函数符号为一个异步函数,这样才干运用 await 关键字
void main() async{
// 运用 await 关键字等候使命履行完结
await Isolate.spawn<String>(subTask, "my task");
print("main func end.");
}
// Isolate 进口函数界说,承受一个 String 参数
void subTask(String msg){
print("subTask receive: $msg ");
}
//打印成果
subTask receive: my task
main func end.
13.2、Isolate 音讯通讯
多个 Isolate 之间只能经过音讯进行通讯,那么咱们如何去获取一个 Isolate 回来的成果呢?
答:首要经过 ReceivePort 和 SendPort 两个类处理音讯通讯
1)、ReceivePort 负责接收 SendPort 发送的音讯, SendPort 和 ReceivePort 是捆绑联系, SendPort 是由 ReceivePort 创立的
void main() async{
// 创立一个 ReceivePort 用于接收音讯
var recv = ReceivePort();
// 创立一个 Isolate,泛型参数为 SendPort,进口函数为 subTask
// subTask 进口函数的参数为 SendPort 类型,因而 spawn 第二个参数,传入 recv 的 sendPort 目标
Isolate.spawn<SendPort>(subTask, recv.sendPort);
// 运用 await 等候 recv 的榜首条音讯
var result = await recv.first;
print("receive:$result");
}
// Isolate 进口函数界说,接收一个 SendPort 目标作为参数
void subTask(SendPort port){
// 运用 SendPort 发送一条字符串音讯
port.send("subTask Result");
}
//打印成果
receive:subTask Result
十四、总结
本篇估计是我写过最长的文章了,比之前写 Kotlin 入门那一篇还要长。总的来说,这篇文章几乎涵盖了 Dart 的一切语法常识,假如你可以耐性看到这儿,并手敲里边的示例,相信你一定收成很大。假如觉得我写的还不错,请给我点个赞吧
感谢你阅览这篇文章
下篇预告
根底打好了,下篇文章咱们就正式进入到 Flutter 的学习了,敬请期待吧
参阅和引荐
一文搞定Dart语法
Dart言语教程
Flutter 根底 | Dart 语法
Dart 官方教程
你的点赞,评论,是对我巨大的鼓舞!
欢迎关注我的大众号: sweetying ,文章更新可榜首时刻收到
假如有问题,大众号内有加我微信的进口,在技能学习、个人生长的道路上,咱们一同前进!