1、变量与常量
1.1 标识符
在Dart中,所有的类名、变量名、方法名都是标识符。
- 标识符称必须由数字、字母、下划线和美元符($)组成。
- 标识符不能以数字开头
- 标识符不能是保留字和关键字
- 标识符名字是区分大小写
- 标识符最好要见名思意 :变量名称建议用名词,方法名称建议用动词
1.2 变量
变量的什么方式大致分为两种:
1、明确声明:明确的指定变量所属的类型。
String str="why";
int num;
bool isRed=true;
2、类型推导:在声明变量时没有明确指定变量的类型,有dart根据赋值的数据类型进行自动推导。
注意:使用类型推导方式声明的变量虽然没有明确指定类型,但是在变量第一次赋值时已经确定了变量的类型,所以在变量重新赋值时只能赋值与第一次赋值时类型相同的数据。
var str="why";
var num;
num=200;
var isRed=true;
1.3 常量
使用final关键字和const关键字修饰的变量称为常量,常量只可以赋值一次。
- final:final修饰的常量可以是一个确定的值或对象,也可以是一个表达式在程序运行时的结果。
- const:const修饰的变量必须在程序编译时就有确定的一个值或对象
- 声明常量是可以省略常量的类型,由赋值行确定。
const int f=10;
const int m=20;
const c=f+m; //数值运算在编译时就可以确定,所以可以赋值
const list=[1,2,3];
//const item=list[1]; 这样就不可以,以为右边的值必须要到程序运行时才能确定。
final p=new Person();
final name=p.getName; //这样可以
//const name=p.getName; //这样不可以
2、数据类型
2.1、int、double、String、bool
2.1.1、类型声明
int 整型:取值范围是 -2^63 ~ 2^63 - 1
int i=100;
int j=300;
double 双浮点型:取值范围是 -2^63 ~ 2^63 - 1
double d=3.1415;
bool 布尔型
//bool,布尔值没有其他语言的非零即真,它的取值只能是true或false;
bool flag1=true;
bool flag2=false;
String 字符串
String str="今天你微笑了吗?";
String sub=str.substring(1,str.length);
print(sub);
字符串的定义
//用双引号或者单引号将文字括起来,就得到了一个字符串
String str1="hello word!";
String str2='hello word!';
String name="张三";
int age=33;
double alipay=1000.9;
double weixin=3000.7;
//字符串拼接,dart使用${表达式}进行字符串的拼接
String str3='My name is ${name} and age is ${age}.';
//在字符串的前面加一个r字符,就会忽略到字符串里面的$符号,得到一个原始字符串
String str4=r'My name is ${name} and age is ${age}.';
//当表达式是一个确定的值的时候,可以省略{}
String str5='My name is $name and age is $age.';
//当表达式不是一个确定的值的时候,{}必须要有
String str6='I have ${alipay+weixin} yuan.';
print(str1);
print(str2);
print(str3);
print(str4);
print(str5);
print(str6);
2.1.2、数据类型的相互转换
int &double
int a=10;
double b=a.toDouble();
a=b.toInt();
print(a);
print(b);
double& String
double age=18.8;
String ageStr=age.toString();
age=double.parse(ageStr);
print(age);
print(ageStr);
int & String
int c=100;
String cc=c.toString();
c=int.parse(cc);
print(cc);
print(c);
2.2、 集合
2.2.1、list 列表
list在Dart中叫做列表,它同时拥有其他语言中的数值和list集合的特性。
- 创建
// list 用法类似于其他语言的数组,但实际上是一个集合
//创建集合,用[]或者new 的方式创建
List list1 = []; //创建一个长度为0的list
List<int> list2 = [4,2,6,7];
List list3 = new List(4); //创建一个长度为4的list,里面的元素都是null
List list4 = new List(0); 创建一个长度为0的list
//List.from()根据一个iterable创建列表
List list5 = List.from(list2);
//castFrom<S,T>() ,将元素为S类型的列表,修改为元素为T类型的列表,这里的元素必须本来就
// 是T类型,相当于将列表中的每个元素都做了强转操作。
//List<String> list6=List.castFrom<int,String>(list2);
list1.add("haha");
list1.add("xixi");
//将cast<T> ,将列表中的元素全部强转为T类型,里面的元素必须本来就是T类型
List<String> list7= list1.cast<String>();
print(list1);
print(list2);
print(list3);
print(list4);
print(list5);
//print(list6);
print(list7);
- 添加元素
//添加元素
list.add(10);
print(list);
//添加另一个list里面的所有元素
List list8 = [4,2,6,7];
list.addAll(list8);
print(list);
- 删除元素
//移除元素
List list=[2,4,6,8,2,4];
list.remove(4);
print2(8,list);
//根据角标移除元素
list.removeAt(4);
print2(9,list);
//移除最后元素
list.removeLast();
print2(10,list);
//移除符合条件的元素
list.removeWhere((e)=> e==8);
print2(11,list);
//removeRange(startIndex,endIndex) ,移除从startIndex到endIndex的元素,包头不包尾
list.removeRange(0, 2);
print2(12,list);
- 遍历
//循环遍历
for(var item in list){
print("item = $item");
}
//使用forEach方法遍历
list.forEach((e){
print("值为:$e");
});
- 排序
//排序,可以指定规则,默认从小到大
list.sort();
print2(15,list);
//匿名函数的返回值大于0为倒序,反之则正序,为0则维持原样
List list9=[4,2,6,7];
list9.sort((a,b) => -(b-a));
print2(16,list9);
- 元素筛选
//从0的位置开始取指定数量的元素
List iterable = list.take(3).toList();
print2(17,iterable);
//从0的位置跳过指定数量的元素,选取剩下的的元素,与take刚好相反
List iterable1 = list.skip(3).toList();
print2(18,iterable1);
//从0的位置开始取符合指定规则的元素,遇到不符合的元素就会停止选取
Iterable iterable2 =list9.takeWhile((e) => e==6);
print2(19,List.from(iterable2));
//从0的位置开始取符合指定规则的元素,遇到不符合的元素就跳过,选取下一个
Iterable iterable3 =list9.where((e) => e==6);
print2(20,List.from(iterable3));
- 元素类型变换
//使用map方法将元素原来的int类型转为String类型
Iterable<String> iterable4 = list.map<String>((e) =>e.toString());
print2(21, List.from(iterable4));
2.2.2、set 集合
//创建Set集合
Set set =new Set();
Set set1 ={};
//Set集合中的元素是不可重复的,我们可以利用Set集合的这个特性对list列表做去重处理。
Set set2 ={1,2,3,2,4};
print(set2);
//给list集合去重
List list=[1,2,2,3,1,4];
list=Set.from(list).toList();
print(list);
set集合与list非常相似,list有的方法set大部分都有。
2.2.3、map 映射
- 创建
//1、使用new 关键字创建
Map map1 = new Map();
Map<String,String> map2 =new Map<String,String>();
//print("map2=> ${map2.runtimeType}");
//2、使用{} 创建
Map<int,Person> map3 ={1:new Man("haha"),2:new Man("张三"),3:new Man("李四")};
//3、通过工厂方法创建,由其他的Map或Iterable变换而来
// castFrom 强制类型转换,与cast类似,Map<K2,V2> map2= map1.cast<K1,V1,K2,V2>();
Map map6=Map.castFrom<int,Person,int,Man>(map3);
print("map6的元素类型为 ${map6.runtimeType}");
//from 以源map中的元素创建新的map,指定泛型必须与源map一致或不指定,键可以为null。
Map<int,String> map7={1:"李四",2:"王五",3:"赵六"};
Map<int,String> map8=Map.from(map7);
print("map8的元素类型为 ${map8.runtimeType}");
print("map8 => $map8");
//fromEntries 从一个iterable实例化一个map
List<MapEntry<int,String>> testList = List<MapEntry<int,String>>();
testList.add(MapEntry(1,"哈哈哈"));
testList.add(MapEntry(2,"嘻嘻嘻"));
Map<int,String> map9=Map.fromEntries(testList); //List是Iterable的子类
print("map9 => $map9");
print("map9的元素类型为 ${map9.runtimeType}");
// of ,键可以为null
Map<int,String> map10 = Map.of(map7);
print("map10的元素类型为 ${map10.runtimeType}");
//fromIterable 根据Iterable创建Map,后面两个参数可以对元素进行变换
List<int> list=[1,2,3,4];
Map map13=Map.fromIterable(list,key:(a)=> a+a,value:(a)=>a*a);
print("map13=> $map13");
//fromIterables 由两个Iterable,第一个提供key,第二个提供value,创建一个Map,
// 两个Iterable长度要相等
List<int> list1=[1,2,3];
List<String> list2=["haha","xixi","hehe"];
Map map14=Map.fromIterables(list1, list2);
print("map14=> $map14");
//4、通过cast、map变换得到
//cast 强制类型转换 Map<K,V> map2= map1.cast<K,V>();
Map<int,Person> map4={1:new Man("haha"),2:new Man("张三"),3:new Man("李四")};
Map<int,Man> map5=map4.cast<int,Man>();
print("map4的元素类型为 ${map4.runtimeType}");
print("map5的元素类型为 ${map5.runtimeType}");
//map ,对Map中的每个MapEntry做修改,得到新的Map集合
Map<int,int> map12 = map4.map<int,int>((k,v){
k=k+1;
return new MapEntry(k, v.name.length);
});
print("map12的元素类型为 ${map12.runtimeType}");
print("map12=> $map12");
//5、创建不可修改的(只读的)Map,需传入另外一个Map,不能传null
Map map15=Map.unmodifiable(null);
print("map15=> $map15");
//6、Map.identity(); 搞不懂
- 添加元素
//创建一个空的map集合
Map map=new Map();
// 使用 putIfAbsent 方法添加元素
// 第三个参数:如果key已经存在,则忽略这一步,如果可以不存在吗,就添加一个新的键值对。
map.putIfAbsent(1, ()=> new Person("haha"));
print("map=> $map");
// 使用addAll添加另一个map中的所有数据
Map mapData={2:"张三",3:"李四"};
map.addAll(mapData);
print("map=> $map");
//使用 addEntries 添加一个以MapEntry为元素的Iterable的所有数据
List<MapEntry> list=[MapEntry(5,"王五"),MapEntry(6,"赵六")];
map.addEntries(list);
print("map=> $map");
- 删除元素
//更据可以来删除元素
map.remove(3);
print("map=> $map");
//删除符合指定条件的元素
map.removeWhere((key,value) => key>5 );
print("map=> $map");
- 修改元素
//第三个参数,如果这个key不存在,那么就添加这个key并且将这个方法的返回值作为value
map.update(7, (value)=> "李倩",ifAbsent: ()=> "张娜娜");
print("map=> $map");
// updateAll 更新所有的value
map.updateAll((k,v){
return "${v.toString()} 去吃饭吗?";
});
print("map=> $map");
- 遍历
//for循环
for(var key in map.keys){
print(map[key]);
}
//forEach
map.forEach((k,v){
print("$k=>$v");
});
3、 函数
3.1 函数的定义
函数的定义和java非常类似,一个完整的函数结构如下:
返回值 函数名(参数类型1 参数1,参数类型1 参数2){
函数体;
}
与java不同的是,dart中函数的返回值在书写时可以省略,这时返回值的类型就是函数体的返回值类型,以下两种写法都是正确的。
int sum1(int a,int b){
return a+b;
}
sum2(int a,int b){
return a+b;
}
当函数的函数体只有一句时,可以使用简写方式:
sum(int a,int b)=> a+b;
3.2 可选参数
在Dart中,函数不能重载,也就是说不管函数的参数是什么,只要同一个类中有同名函数,就会报错,为了解决传入不同参数的需求,引入了可选参数的概念,可选参数可以设置默认值。
可选参数又有位置可选参数和命名命名可选参数两种,可选参数必须声明在函数的参数列表的最后面。
- 位置可选参数:可选参数列表有[]包裹,在调用函数时传入的参数顺序必须与函数的参数列表一致。
/** 结构:
返回值 函数名(参数类型1 参数1,[参数类型2 参数2 = 默认值,参数类型3 参数3 = 默认值]){
函数体;
}*/
//声明
String join(String str1,[String str2,String str3="张三"]){
return "names=>$str1,$str2,$str3";
}
//调用
join("王五","赵六");
join("王五","赵六","胡汉三");
- 命名可选参数:可选参数列表有{}包裹,在调用函数时传入的参数需要用对应的参数名指明,与参数顺序无关。
//声明
String join2(String str1,{String str2,String str3="张三"}){
return "names=>$str1,$str2,$str3";
}
//调用
join2("王五",str2:"赵六",str3:"李琪琪");
3.3 函数是一等公民(高级函数)
在Dart中,一个函数可以作为另一个函数的参数或者返回值进行传递。比如:
main(){
num(20,30,(res){
String str="res= $res";
print(str);
return res;
});
}
void num(int a,int b,String fun(int res)){
int res=a+b;
String str=fun(res);
print("str= $str");
}
作为函数的参数可以写成匿名函数直接在传参时声明。
main(){
num(20,30,(res){
String str="res= $res";
print(str);
return str;
});
}
void num(int a,int b,String fun(int res)){
int res=a+b;
String str=fun(res);
print("str= $str");
}
有些函数的声明书写很复杂,直接写在参数列表可读性非常差,这时我们可以使用typedef关键字对函数的声明起一个别名,这时这个别名就是一个新的类型,可以代表这个函数的类型。
main(){
num(20,30,(res){
String str="res= $res";
print(str);
return str;
});
}
void num(int a,int b,PrintResult fun){
int res=a+b;
String str=fun(res);
print("str= $str");
}
typedef PrintResult = String Function(int res);
4 、运算符
4.1 算术运算符(+、-、*、/、~/)
int a = 10;
int b = 2;
//加
print(a + b);
//减
print(a - b);
//乘
print(a * b);
//除
print(a / b);
//取整
int result = a ~/ b;
print(result);
//取余
print(a % b);
4.2 自增、自减(++、–)
//自增
//前++,先给函数打印,后自增
print(a++);
//后++,先自增,再给函数打印
print(++a);
//先给函数打印,再自减
print(a--);
//先自减,再给函数打印
print(--a);
4.3 关系运算符(>、<、>=、<=、==)
int a = 5;
int b = 3;
//判断是否相等
print(a == b);
//判断是否不相等
print(a != b);
//是否大于
print(a > b);
//是否小于
print(a < b);
//是否大于等于
print(a >= b);
//是否小于等于
print(a <= b);
//双等号判断内容相等
String strA = '123';
//String strB = '321';
String strB = '123';
print(strA == strB);
4.4 逻辑运算符(&、&&、|、||、!)
bool isTrue = true;
//取反
print(!isTrue);
//并且
bool isFalse = false;
print(isTrue && isFalse);
//或者
print(isTrue || isFalse);
//日常取反用途
String str = "";
print(!str.isEmpty);
& 和 &&,| 和 || 的区别:
&:不管&的左边是否为真,都会判断右边是否为真
&&:如果&的左边是否为假,直接判断定结果为假,不用去看右边的真假
|:不管&的左边是否为真,都会判断右边是否为真
||:如果&的左边是否为真,直接判断定结果为真,不用去看右边的真假
4.5 赋值运算符(=、+=、-=、*=、/=、??=)
- 复合运算符
double a = 10;
//int b;
int b = 5;
//复合运算符
a += 2;
print(a);
a -= b;
print(a);
a *= b;
print(a);
print(a /= b);
print(a %= b);
- ??= ,如果变量没有赋值才进行赋值,否则不进行赋值。
//??=,如果变量没有赋值才进行赋值,否则不进行赋值
b ??= 10;
print(b);
- 条件运算符(?、??)
//? 三目运算符:当左边的表达式为真时,使用:左边的值,否则使用:左边的值
//int gender = 0;
int gender = 1;
//String str = gender == 0 ? 'Male' : 'Female';
//还可以加上插值表达式
String str = gender == 0 ? 'Male=$gender' : 'Female=$gender';
print(str);
//?? ,当??左边的值为null时,使用右边的值赋值,否则使用左边的值赋值。
String a;
//String a = 'Dart';
String b = "Java";
//a为null,所以会使用b的值返回
String c = a ?? b;
print(c);
5 面向对象
面向对象是一种编程思想,用来描述一类事物的方式,包括事物的特征和行为方式。
5.1 类
- 类的声明:
/**
类的声明:
class 类名 {
类的主体
}
*/
class Animal{
//动物属性:动物的重量
int weight;
//动物属性:动物的年龄
int age;
//类的构造方法,用来创建这个类的对象
//this关键字:代表当前的这个对象
Animal(int weight,int age){
this.weight=weight;
this.age=age;
}
//类的成员方法,
void eat(){
print("动物的行为:吃吃吃");
}
}
在dart中,当类中没有声明构造函数时,会自动生成一个无参数的构造函数,当类中已经声明了构造函数,就不会生成。构造函数可以简写:
Animal(this.weight,this.age);
- 创建类的对象并使用
//使用类的构造方法来创建一个对象
final animal= new Animal(20, 2);
//在dart中,关键字 new 可以省略
final animal2= Animal(20, 2);
animal2.eat();
- 命名构造方法:在dart中,函数不能重载,为了满足不同参数构造对象的需求,需要定义命名构造函数。
Animal.instance(int weight,int age){
this.weight=weight;
this.age=age;
}
调用
//使用工厂构造方法来创建一个对象
final animal= new Animal.instance(20, 2);
animal.eat();
//在dart中,关键字 new 可以省略
final animal2= Animal.instance(20, 2);
animal2.eat();
- 常量构造函数:
拥有常量构造函数的类中的所有的属性必须是使用final修饰的常量,常量构造函数中不能使用this关键字
main(){
const p=Person("张三", 34);
p.speek();
print(p.name);
}
class Person{
final String name;
final int age;
const Person(this.name, this.age);
void speek()=> print("讲汉语");
}
- 工厂构造函数:使用factory关键字修饰,里面只能使用静态的成员,按情况返回构建的对象。
main(){
var p=Person("张三",34);
p.speek();
print("p.name=${p.name} p.age=${p.age}");
}
class Person{
String name;
int age;
//对构建的Person做一个缓存
static Map<String,Person>map=new Map();
Person.name(this.name, this.age);
factory Person(String name, int age){
if(name==null){
return null;
}
Person p;
//当缓存中已经存在这个名字对应的Person对象时,直接返回这个对象
if(map.containsKey(name)){
p=map[name];
}else{
p=new Person.name(name, age);
}
return p;
}
void speek()=> print("讲汉语");
}
- 构造函数的初始化列表:
a、初始化列表会在构造函数的方法体之前执行;
b、初始化列表多用于设置final关键字修饰的字段。
c、在构造函数的()后面以冒号开头,表达式之间以逗号隔开。
class Animal{
//动物属性:动物的重量
int weight;
//动物属性:动物的年龄
int age;
//动物属性:动物的颜色
final color;
//动物属性:动物的食物
final food;
//类的构造方法,用来创建这个类的对象
//this关键字:代表当前的这个对象
//Animal(this.weight,this.age);
//普通构造方法
Animal(this.weight,this.age,this.food):color="red";
//工厂构造方法
Animal.instance(int weight,int age):color="red",food="草"{
this.weight=weight;
this.age=age;
}
//类的成员方法,
void eat(){
print("动物的行为:吃$food");
}
}
- getter、setter 方法:
Dart的getter、setter方法与java中有所不同。
main(){
var person = new Person("二哈");
print(person.getName);
person.setName="翠花";
print(person.getName);
}
class Person{
String name;
Person(this.name);
//getter 方法
String get getName => name;
//setter 方法
set setName(String value) =>name = value;
}
5.2 类的继承
- 使用extends关键字声明子类继承父类。
- 子类必须在构造函数的初始化列表中使用super关键字调用父类的构造函数以初始化。
- 子类可以使用父类的属性和方法,但是以下划线“_”开头的属性和方法只有在同一个Dart文件内才能使用。
main(){
var person = new Man("二哈");
print(person.name);
}
class Person{
String name;
Person(this.name);
}
//使用extends关键字使Man类继承Person类
class Man extends Person{
final String food;
//子类必须在构造函数的初始化列表中使用super关键字调用父类的构造函数以初始化。
Man(String name):food="米饭",super(name);
}
5.3 抽象类
- 使用abstract关键字修饰的类称为抽象类
- 抽象类中的方法可以有方法体,也可以没有,没有方法体的方法称为抽象方法
- 如果一个抽象类的子类不是抽象类,那么该子类必须实现父类的所有抽象方法
abstract class Person{
String name;
Person(this.name);
void eat();
}
//使用extends关键字使Man类继承Person类
class Man extends Person{
final String food;
//子类必须在构造函数的初始化列表中使用super关键字调用父类的构造函数以初始化。
Man(String name):food="米饭",super(name);
@override
void eat() {
print("大口大口的吃$food");
}
}
5.4、 接口
在Dart中没有专门的关键字用来定义接口,所有的类都可以当做接口来使用。
- 使用implements 关键字来声明要实现的接口
- 实现类必须实现或重写接口类中的所有的方法
- 实现类可以同时实现多个接口
main(){
var c = new C();
c.testA();
c.testB();
}
class A {
void testA(){
print("test-->A.a");
}
}
class B{
void testA(){
print("test-->B.a");
}
void testB(){
print("test-->B.b");
}
}
class C implements A,B{
@override
void testA() {
print("test-->C.a");
}
@override
void testB() {
print("test-->C.b");
}
}
打印结果:
test-->C.a
test-->C.b
5.5 混入(mixins)
混入是java中没有新语法,具有以下特点:
- 使用with关键字声明需要混入的类
- 可以在当前类使用混入类的属性和方法,但是以下划线“_”开头的属性和方法只有在同一个Dart文件内才能使用。
- 可以同时混入多个类,类之间使用逗号隔开
- 如果被混入的类中有抽象方法,那么必须进行实现
- 如果类或被混入的类中存在同名函数,后混入的类的优先级更高,当前的类优先级最高
main(){
var c = new C();
c.testA();
c.testB();
c.testC();
c.test();
print(c.name);
}
class A {
final name="张三";
void testA(){
print("test-->A.a");
}
void testB(){
print("test-->A.b");
}
void testC(){
print("test-->A.c");
}
}
abstract class B{
void testA(){
print("test-->B.a");
}
void testB(){
print("test-->B.b");
}
void test();
}
//使用with声明要混入的类A,B
class C with A,B{
void testA(){
print("test-->C.a");
}
//实现混入类B中的抽象方法test()
@override
void test() {
print("test-->test");
}
}
打印结果:
test-->C.a
test-->B.b
test-->A.c
test-->test
张三
5.6 枚举(enum)
枚举的用途:限制参数的取值范围,以保证类型安全,(比如颜色、性别、星期等)
- 使用enum关键字+枚举的名称进行申明
- 多个枚举值之间用逗号隔开
- 枚举值最好都用大写
main(){
var person = new Person("张三", Sex.MAN);
print(person);
//使用Sex.values获取所有的枚举值
print(Sex.values);
}
enum Sex{
MAN,WOMEN
}
class Person{
String name;
Sex sex;
Person(this.name, this.sex);
@override
String toString() {
return "$name is $sex";
}
}
5.7 external 外部扩展
external 关键字用来声明外部扩展的函数,使在类中只有函数的声明,具体的实现交给外部的另一个库去实现,多用于对不同的平台做不同的实现。