一、Dart中的变量
1.1、变量
var :解释器自动推断变量的类型。注意:再次赋值的类型不一样会报错
dynamic:变量类型可动态改变
final:最终变量,不可变的变量。在声明时,需要对变量进行赋值
const:常量,不可变的变量。在声明时,需要对变量进行赋值
注意:无论什么类型的变量,如果不对其进行赋值,那么它的默认值都是 null。
// 以下代码会报错:
var name = "变量";
name = 1;
// 以下代码不会报错
dynamic age = 26;
age = "26";
进行推断
1.2 变量的数据类型
Dart 中内置了 7 类特殊的数据类型:
numbers:数值类型
strings:字符串类型
booleans:布尔类型
lists:列表类型
maps:图类型
runes:字符类型
symbols:符号类型
1.2.1 数值类型
在 Dart 中,数值类型有两种,分别为 int 和 double。
如果使用 0x 开头定义数值,就表示使用十六进制数值。
如果一个数值包含小数,就需要将其定义为 double 类型。
e 符号表示科学计数法。
整型数值是可以直接赋值给浮点型变量的。
int a = 99;
int b = 0xA1;
print(a);//99
print(b);//161
double c = 3.14;
double d = 1.4e2;
print(c);//3.14
print(d);//140.0
double e = 1;
print(e);//1.0
数值类型常用属性
var count1 = 1;
var count2 = 1.1;
//runtimeType 属性获取运行时类型
print(count1.runtimeType);//int
print(count2.runtimeType);//double
//获取当前数值是否为有限值,返回 true 或者 false
print(count1.isFinite);
//获取当前数值是否为无限值,返回 true 或者 false
print(count1.isInfinite);
//获取当前数值是否为 NaN,NaN 描述非数值
print(count1.isNaN);
//获取当前数值是否为负数
print(count1.isNegative);
//获取当前数值的符号。若返回 1,则表示为正数;若返回-1,则表示为负数;若返回 0,则表示当前数值为 0
print(count1.sign);
//获取存储当前数值需要的最少位数,int 类型独有的属性
print(count1.bitLength);
//获取当前数值是否为偶数,int 类型独有的属性
print(count1.isEven);
//获取当前数值是否为奇数,int 类型独有的属性
print(count1.isOdd);
数值类型常用方法
var count1 = -1;
var count2 = 1.1;
//返回当前数值的绝对值
print(count1.abs());
//返回不小于当前数值的最小整数
print(count2.ceil());//2
//返回不小于当前数值的最小整数,返回数值为浮点类型
print(count2.ceilToDouble());//2.0
//返回指定范围内离当前数值最近的数值,如果当前数值在范围内,就直接返回
print(count1.clamp(1,10));
//将当前数值与传入的参数进行比较:如果大于传入的参数,就返回 1;如果小于传入的参数,就返回-1;如果等于传入的参数,就返回 0
print(count1.compareTo(0));
//返回不大于当前数值的最大整数
print(count2.floor());//1
//返回不大于当前数值的最大整数,返回数值为浮点类型
print(count2.floorToDouble());//1.0
//获取当前数值除以参数后的余数
print(count1.remainder(5));
//获取离当前数值最近的整数,四舍五入
print(count2.round());
//获取离当前数值最近的整数,四舍五入,返回浮点数
print(count2.roundToDouble());
//将当前数值转换成浮点数返回
print(count1.toDouble());
//将当前数值转换成整型数返回
print(count2.toInt());
//将当前数值转换成字符串返回
print(count1.toString());
//将当前数值的小数部分丢弃后返回整数值
print(count2.truncate());
//将当前数值的小数部分丢弃后返回整数值,浮点类型
print(count2.truncateToDouble());
int类型独有的方法
var num1 = 35;
//获取当前数与传入参数的最大公约数
print(num1.gcd(7));//7
//求模逆运算
print(num1.modInverse(6));
num1 = 2;
//对当前数值进行幂运算,之后进行取模运算
print(num1.modPow(3,5));//2 的 3 次方除以 5 取余数
1.2.2 字符串类型
可以使用单引号或者双引号来创建字符串
$来进行字符串的格式化
字符串也支持直接使用“+”运算符来进行拼接
即使不使用运算符,相邻的字符串也会自行进行拼接
使用 3 对单引号或者 3 对双引号可以进行多行字符串的创建
反斜杠进行字符转译
var name = "变量";
var str3 = "Hello ${name} $name";
var num1 = 3;
var num2 = 4;
var str4 = "3+4=${num1+num2}";
print(str3);//Hello 变量
print(str4);//3+4=7
// “+”运算符来进行拼接
var str1 = "Hello";
var str2 = 'World';
print(str1+str2);//HelloWorld
// 相邻的字符串自行进行拼接
print('hello''world');//helloworld
// 3 对单引号或者 3 对双引号创建多行字符串
var str5 = '''第一行
第二行
第三行''';
var str6 = """第一行
第二行
第三行""";
print('hello \'变量\'');//hello '变量'
常用属性和方法
//获取字符串的字符码集合
print(str8.codeUnits);//[97, 98, 99]
//获取当前字符串是否为空字符串,返回布尔值
print("".isEmpty);//true
//获取当前字符串是否为非空,返回布尔值
print("".isNotEmpty);//false
//获取当前字符串长度
print(str8.length);//3
//获取类型
print(str8.runtimeType);//String
//通过下标获取某个字符串中某个字符的 code 码,下标从 0 开始
print("hello".codeUnitAt(0));//104
//进行字符串比较,逐个字符进行 code 码的比较
print("hello".compareTo('a'));//1
//获取当前字符串是否包含参数字符串
print("hello".contains('l'));//true
//判断当前字符串是否以某个字符串结尾
print("hello".endsWith("llo"));//true
//判断当前字符串是否以某个字符串开头
print("hello".startsWith('h'));//true
//获取要进行匹配的字符串在当前字符串中的位置,如果没找到,就返回-1
print("hello".indexOf("l"));//2
//获取要进行匹配的字符串在当前字符串中的位置,逆向查找,如果没找到,就返回-1
print("hello".lastIndexOf("l"));//3
//在左边进行字符串位数补齐
print("hello".padLeft(10,"*"));//*****hello
//在右边进行字符串位数补齐
print("hello".padRight(10,"&"));//hello&&&&&
//进行字符串替换,将匹配到的字符串替换成指定的字符串
print("hello".replaceAll("o","p"));//hellp
//将指定范围内的字符串进行替换,左闭右开区间
print("hello".replaceRange(0,3,"000"));//000lo
//使用指定字符串作为标记对原字符串进行分割,结果会放进列表返回
print("hello".split('e'));//[h, llo]
//进行字符串截取,左闭右开区间
print("hello".substring(1,3));//el
//将字符串全部转为小写
print("Hello".toLowerCase());//hello
//将字符串全部转为大写
print("hello".toUpperCase());//HELLO
//将字符串首尾的空格去掉
print(" hello ".trim());
//将字符串首部的空格去掉
print(" hello".trimLeft());
//将字符串尾部的空格去掉
print("hello ".trimRight());
// 对字符串进行拷贝,使用*运算符
print("hello"*2);//hellohello
// 使用中括号来获取集合内的某个元素
print("hello"[0]);//h
1.2.3 布尔类型
一种简单的数据类型,其只有两个字面量值:true 和 false。
使用 runtimeType 来获取类型
调用 toString 方法来将布尔值转换成字符串
bool a = true;
bool b = false;
print(a.runtimeType);//bool
bool a = true;
print(a.toString());//true
1.2.4 列表类型
列表用来存放一组数据,也被称为数组。在 Dart 中,列表具体的数据类型由其中的元素类型决定。
想在列表中存放不同类型的数据,则可以将列表声明成动态类型的。
列表类型可以通过构造方法来创建
下标超出了列表元素的个数,就会产生溢出异常。
List<int> list = [1,2,3,4];
List<dynamic> list = [1,2,3,4,"5"];
//创建长度为 5 的列表,默认使用 null 填充
var list2 = new List(5);//new 可以省略 [null, null, null, null, null]
//创建指定长度的列表,并使用指定的值作为默认值
var list3 = List.filled(3,1);//[1,1,1]
//通过另一个集合类型的数据来创建列表
var list4 = List.from(list3);//[1,1,1]
//获取列表的第一个元素
print([1,2].first);//1
//获取列表中的最后一个元素
print([1,2].last);//2
//获取列表的长度
print([1,2].length);//2
print(["a","b","c","d"][3]);//d
print([1,2]+[2,3]);//[1, 2, 2, 3]
var data = [1,2,3];
data[2] = 4;
print(data);//[1, 2, 4]
List 类中封装了大量的实例方法,这些方法可以极大地提高开发者的工作效率
var l = [];
//向列表中增加元素
l.add(1);
//向列表中增加一组元素
l.addAll([2,3]);
//将列表对象映射成字典对象,下标为键,元素为值
print(l.asMap());//{0: 1, 1: 2, 2: 3}
//将列表中某个范围的元素进行覆盖
l.fillRange(0,2,"a");//[a, a, 3]
//获取列表某个范围内的元素集合
print(l.getRange(0,3));
//获取列表中某个元素的下标,从前向后找,如果没有,就返回-1
print(l.indexOf('a'));
//获取列表中某个元素的下标,从后向前找,如果没有,就返回-1
print(l.lastIndexOf("a"));
//向列表中的指定位置插入一个元素
l.insert(0,'s');//[s, a, a, 3]
//向列表的指定位置插入一组元素
l.insertAll(0,["a","b","c"]);//[a, b, c, s, a, a, 3]
//删除列表中的指定元素,从前向后找到第一个删除
l.remove("a");
//删除列表中指定位置的一个元素
l.removeAt(0);
//删除列表中的最后一个元素
l.removeLast();
//删除列表中指定范围内的元素
l.removeRange(0,2);
//将列表中指定范围的元素进行替换,替换为集合参数中的元素
l.replaceRange(0,2,[1,2,3,4]);
//截取列表中范围内的元素返回新的列表
print(l.sublist(0,3));
//判断列表中是否包含指定元素
print(l.contains(2));
//使用指定拼接符将列表拼接为字符串
print(l.join("-"));//1-2-3-4
//将列表转换为字符串
print(l.toString());//[1, 2, 3, 4]
print(l);
//删除列表中所有的元素
l.clear();
1.2.5 字典类型
字典是一组键值对的集合,通过键可以完成对值的修改、
查找、添加或删除。在 Dart 中,字典类型叫作 Map
//键为字符串类型、值为整数类型的字典
Map<String,int> map2 = {"1":1,"2":2};
//使用构造方法创建字典
var map3 = Map();
//新增键值对
map3["name"] = "珲少";
print(map3["name"]);//珲少
//修改键值
map3["name"] = "Lucy";
print(map3["name"]);//Lucy
//不存在的键值将返回 null
print(map3["age"]);//null
//将某个键的值置为 null,并不会将此键值对删除
map3["name"] = null;
print(map3["name"]);//null
Map 对象中常用的属性列
//判断 Map 是否为空
print({"1":1,"2":2}.isEmpty);//false
//判断 Map 是否为非空
print({"1":1,"2":2}.isNotEmpty);//true
//获取 Map 所有的键
print({"name":"Lucy","age":25}.keys);//(name, age)
//获取 Map 所有的值
print({"name":"Lucy","age":25}.values);//(Lucy, 25)
//获取 Map 中键值对的个数
print({"name":"Lucy","age":25}.length);//2
//获取类型
print({"name":"Lucy"}.runtimeType);//_InternalLinkedHashMap<String, String>
Map 类中的实例方法
var map = {};
//向 Map 中追加键值对
map.addAll({"name":"Lucy","age":28});//{name: Lucy, age: 28}
//判断 Map 中是否包含某个键
print(map.containsKey("name"));//true
//判断 Map 中是否包含某个值
print(map.containsValue("Lucy"));//true
//删除某个键值对
map.remove("name");
//将 Map 转换成字符串
print(map.toString());
//清空 Map 中的键值对
map.clear();
二、Dart中的运算符
2.1 算数运算符
算数运算符通常用来进行简单的数据运算,例如加、减、乘、除等。
print(1+2);//3
print("1"+"2");//12
print(3-1);//2
print(-(-1));//1
print(2*4);//8
print(10/2);//5.0
// 整除
print(9~/2);//4
// 取模运算符
print(9%2);//1
// 自加和自减
var a = 3;
a++;
print(a);//4
++a;
print(a);//5
2.2 比较运算符
比较运算符的作用是进行两个值的比较。虽然在 Dart 中,比较运算符的操作数可以是任意类型的值,但是对于 List、Map 或 String 对象,一般会使用函数来进行比较,比较运算符更多用于数值之间的比较。可用的比较运算符如下表所示。
2.3 类型运算符
Dart 中的类型运算符有 3 种:as、is 和 is!。
as 运算符用来进行类型的“转换”,需要注意,这里的类型转换并不是真正意义上的转换,其并不会真正修改数据的类型,而是告诉 Dart将当前数据当成某个类型的数据来进行处理。在面向对象开发中,这个运算符非常有用,后面我们会介绍。
is 运算符用来判断数据是否属于某个类型:如果属于,就返回布尔值 true;如果不属于,就返回布尔值 false。
is!运算符的作用则与 is 刚好相反,它用来判断数据是否不属于某个类型,示
例如下:
var a = 1;
var b = "2";
print(a is int);//true
print(b is! String);//false
2.4 复合运算符
简单理解,复合运算符是多种简单运算符的复合。复合运算符通常也叫作赋值复合运算符,因为其总是一种运算符与赋值运算符的组合。
2.5 逻辑运算符
逻辑运算符是针对布尔值进行运算的运算符。我们知道,布尔值只有两种:true 和 false。逻辑运算符所适用的操作数也只有 true 或者 false。
“!”被称为逻辑非运算符,进行逻辑非运算,它是一个前置运算符,且只有一个操作数,当操作数为布尔值 true 时,运算结果为布尔值 false,当操作数为布尔值 false 时,运算结果为布尔值true。
“||”被称为逻辑或运算符,进行逻辑或运算。逻辑或运算遵守下面的运算规则:
(1)当两个操作数中至少有一个操作数为 true 时,运算结果为 true。
(2)当两个操作数都为 false 时,运算结果才为 false。
“&&”被称为逻辑与运算符,进行逻辑与运算。逻辑与运算遵守下面的运算规则:
(1)当两个操作数中至少有一个操作数为 false 时,运算结果为 false。
(2)当两个操作数都为 true 时,运算结果为 true。
print(!false);//true
print(!true);//false
print(false||false);//false
print(false||true);//true
print(true||false);//true
print(true||true);//true
print(false&&false);//false
print(true&&false);//false
print(false&&true);//false
print(true&&true);//true
2.6 位运算符
位运算符是对二进制位进行操作的运算符。在计算机中,所有的数据存储实际上采用的都是二进制。
“&”用来进行按位与运算。所谓按位与运算,是指将两个运算符的每一个二进制位分别进行与运算,即若两个对应二进制位都为 1,则运算结果为 1,否则为 0。
“|”用来进行按位或运算。与按位与运算一样,按位或运算将两个运算数的每个二进制位分别进行或运算,若两个对应二进制位有一个为 1,则运算结果为 1,否则运算结果为 0。
“~”用来进行按位非运算。按位非运算是一个前置的单元运算符,其只有一个操作数,对操作数的每一个二进制位进行取反,即为 0 的位运算后结果为 1,为 1 的位运算后结果为 0。
“^”用来进行按位异或运算。关于按位异或运算,只需要牢记进行运算的两个数位相同时,运算结果为 0,否则运算结果为 1 即可,即两个二进制位都为 0 或者都为 1 时,运算结果为 0,否则运算结果为 1。
“<<”用来进行按位左移运算,即将每一个二进制位向左移动指定位数。对于二进制数据,
一个很重要的特点是每左移一位,会使原数值进行乘 2 操作
“>>”用来进行按位右移操作
var a = 10; //二进制 1010
var b = 3; //二进制 0010
print(a&b);//2 即二进制 0010
var c = 10; //二进制 1010
var d = 4; //二进制 0100
print(c|d);//14 即二进制 1110
var e = 4; //00000100
print(~e); //11111011 以补码表示 原码为 00000101 且为负数 即-5
var f = 3; // 0011
var g = 5; // 0101
print(f^g);// 0110 十进制 6
var h = 3;//0011
print(h<<1);//0110 十进制 6
var i = 4;//0100
print(i>>1);// 0010 十进制 2
2.7 条件运算符
条件运算符与流程控制语句中的条件语句作用很像.
“?:”是一个三元的运算符,其有 3 个操作数,第一个操作数可以是一个布尔值或者运算结果为布尔值的表达式,
当这个操作数为 true 时,条件运算的结果为第二个操作数的值,当第一个操作数为 false 时,条件
运算的结果为第三个操作数的值。
“??”是 Dart 中的空条件运算符,其有两个操作数,若第一个操作数为 null,则运算后的值为第二个操作数的值,若第一个操作数为非 null 值,则运算后的值为第一个操作数的值。这个运算符最大的作用是保证运算的结果不为 null 值,通常用来进行安全保证。
var a = 3;
var b = 5;
var res = a>b ? "a>b" : "a<=b";
print(res);//a<=b
var c = null;
print(c==null?"无作为":"额外操作 a:$c");
var c = 3;
print(c??"无作为");
var c = null;
c ??= 0;//与 c = c??0;意义完全一样
2.8 级联运算符
级联运算符使用“..”表示,级联运算符是 Dart 中比较高级的一种运算符,它可以让开发者对某个对象连续地进行一系列操作。这样的好处是可以减少中间变量的生成,并且让开发者更畅快地体验 Dart 编码的乐趣。
var p =People()..name="珲少"..age=26;
print("name:${p.name},age:${p.age}");
2.9 点运算符
点运算符用来对对象的属性和方法进行操作。
class People {
String name;
int age;
void printSelf(){
print("name:${name},age:${age}");
}
}
main() {
var p = People();
p.name = "珲少";
p.age = 26;
p.printSelf();//name:珲少,age:26
}
补充,条件成员访问运算符“?.”。这个运算符的作用是,如果所调用的对象是非 null 值,就会正常进行访问,否则返
回 null,但是不会产生错误
var c = null;
print(c?.a);//null
3 Dart 中的流程控制语句
条件语句:if-else
循环语句:while,do-while,for,for-in
中断语句:break,continue
多分支条件语句:if-else if-else,switch-case
异常处理:throw,try-catch-finally
补充:
while 语句会首先进行循环条件的判定,如果不满足,
就不再执行循环体,满足条件才会进行循环;
do-while 语句则是首先执行一次循环体中的代码,之后进行循环条件的判定,如果满足,就继续执行循环体,如果不满足,就跳出循环
break 语句会直接跳出本层循环,执行循环后面的代码,而 continue 语句则是跳过本次循环后,还会进行循环条件的判定
3 Dart 高级进阶
3.1构造方法
一旦重写了构造方法,默认的无参构造方法将不再可用。
构造方法的实质是将对象属性的赋值过程由外界封装到类的内部。
构造方法的编写还有一个小技巧,一般情况下,我们可以直接将构造方法定义成如下模样,Dart 会自动进行参数和属性的匹配,进行赋值,非常方便.
main() {
var circle = new Circle(6,1,1);//使用参数构造圆形对象
print(circle.radius);//6.0
}
class Circle {
//半径
double radius = 0;
//圆心 X
double centerX = 0;
//圆心 Y
double centerY = 0;
//构造方法
Circle(this.radius,this.centerX,this.centerY);
}
一个类需要有多个构造方法,比如自定义的圆形类,很多时候需要快速创建出单位圆(圆心为坐标原点、半径为 1 的圆)。这时,就可以定义一个便捷的构造方法帮助我们直接生成单位圆,这类构造方法也被称为命名构造方法.
main() {
var circle2 = Circle.standard();
print(circle2.radius);//1
}
class Circle {
//半径
double radius = 0;
//圆心 X
double centerX = 0;
//圆心 Y
double centerY = 0;
//构造方法
Circle(this.radius,this.centerX,this.centerY);
//命名构造方法,单位圆
Circle.standard(){
this.radius = 1;
this.centerX = 0;
this.centerY = 0;
}
}
命名构造方法通常用来快速地创建标准对象,同样,命名构造方法也可以有参数,并且只要参数名与类中定义的属性名一致,也可以使用 Dart 自动匹配赋值的特性。
3.2实例方法
类封装了属性和方法,属性用来存储描述类的数据,方法用来描述类的行为。
方法的用法和函数一样,只是在调用时需要用对象来调用,并且方法中会自动将当前对象绑定到 this 关键字上。也就是说,在方法中可以通过 this 关键字获取对象的属性信息,也可以调用其他方法。方法也需要通过点语法来进行调用。
Setters 方法与 Getters 方法
Setters 方法用来设置对象属性,Getters 方法用来获取对象属性。其实当我们使用点语法访问对象属性信息时,调用的就是 Setters方法或 Getters 方法,在定义属性时,Dart 会自动生成默认的 Setters 方法和 Getters 方法。Setters方法和 Getters 方法的另一大作用是定义附加属性,附加属性也可以理解为计算属性,即这些数据通常不是描述对象的最原始数据,而是通过计算得来的,例如下面的description就是附加属性:
main() {
var teacher = Teacher("珲少",1101,"Dart");
teacher.sayHi("小明"); //Hello 小明,我是珲少老师!编号为1101
teacher.teaching(); //珲少老师正在进行 Dart 教学
print(teacher.description); //珲少:Dart
teacher.description = "Lucy:JavaScript";
teacher.teaching(); //Lucy 老师正在进行 JavaScript 教学
}
class Teacher {
String name;
int number;
String subject;
Teacher(this.name,this.number,this.subject);
void sayHi(String toName){
print("Hello ${toName},我是${this.name}老师!编号为${this.number}");
}
void teaching(){
print("${this.name}老师正在进行${this.subject}教学。");
}
String get description{
return "${this.name}:${this.subject}";
}
set description(String value){
this.name = (value.split(":") as List)[0];
this.subject = (value.split(":") as List)[1];
}
}
上面的代码中,description 就是附加属性,其并没有真正占用内存空间进行存储,而是通过其他属性计算而来的
3.3 抽象类与抽象方法
抽象类,是面向对象开发中较为难理解的一点。在 Dart 中,抽象类中可以定义抽象方法。
抽象方法,是指只有定义却没有实现的方法,抽象是面向接口开发的基础。
抽象类实际上就是一个接口,接口中定义了未实现的方法告诉调用者:如果有类实现了这个接口,这个类就拥有接口所描述的功能。例如,我们可以为教师类定义一个接口,示例如下:
abstract class TeacherInterface {
void teaching();
}
上面的 TeacherInterface 接口中只定义了一个抽象方法,Teacher 类可以对这个接口进行实现,示例代码如下:
abstract class TeacherInterface {
void teaching();
}
class Teacher implements TeacherInterface {
String name;
int number;
String subject;
Teacher(this.name,this.number,this.subject);
void sayHi(String toName){
print("Hello ${toName},我是${this.name}老师!编号为${this.number}");
}
void teaching(){
print("${this.name}老师正在进行${this.subject}教学。");
}
}
一个类也可以同时实现多个接口,例如再定义一个人类接口,示例如下:
abstract class TeacherInterface {
void teaching();
}
abstract class PeopleInterface {
void sayHi(String name);
}
class Teacher implements TeacherInterface,PeopleInterface {
String name;
int number;
String subject;
Teacher(this.name,this.number,this.subject);
void sayHi(String toName){
print("Hello ${toName},我是${this.name}老师!编号为${this.number}");
}
void teaching(){
print("${this.name}老师正在进行${this.subject}教学。");
}
}
抽象类不可以被实例化,即不能直接使用抽象类来构造实例对象,只能通过实现这个抽象类接口的类或者继承它的子类来实例化对象。
遗留问题:类实现了抽象类,则类中必须重写抽象类中的方法吗?