Dart数据类型
变量
dart可以用var关键字从而通过值类型来声明变量(自动推断)
var str1 ='helloworld'; var i1 = 0;
dart也可以直接用类型来声明变量
String str2 = 'hello';//Stirng首字母大写 int i2 = 0;
命名规则:
-
变量名称必须要有数字、字母、下划线和美元$组成
-
标识符开头不能是数字
-
标识符不能是保留字和关键字
-
变量的名字区分大小写
-
标识符要见名知意,变量名称建议名词,方法名称建议用动词
常量
不可以修改
-
const:编译时常量,不可修改
-
final:运行时常量,并且final时惰性初始化,即在运行时第一次使用前才初始化
final a = new DateTime.now();//运行时初始化才可以这么使用 print(a);?//打印出现在时间
数据类型
建议在定义的时候指定变量的类型
-
int:整型
-
bouble:双精度浮点型
-
String:字符串
-
bool:布尔,值为
true
或false
-
List:数组(列表)
新版本的dart不支持用new创建列表
四种创建方式:
//未指定列表内类型 var l1 = ['张三',20,true]; print(l1[0]); print(l1);//打印整个列表 print(l1.length);//输出列表长度 //指定好列表内的类型 var l2 = <String>['张三', '李四', '王五']; print(l2); print(l2[2]); print(l2.length); //空列表 var l4 = [];//长度为0 //创建固定长度的列表 var l5 = List.filled(length,fill);//fill为填充的内容
用
[]
定义的列表长度是可以变化的var l4 = []; l4.add("张三");//通过.add增加数据
用filled定义的列表长度不可以改
不可以用
.length=
和.add
来改变列表长度 -
Maps:类(对象)
//给出内容定义 var person={ "name":"张三", "age":"20", "work":["程序员","外卖员"] }; print(person);//打印出这个json对象 print(person["name"]);//访问不是用. //用new关键字定义 var p = new Map(); p["name"]="李四";//赋值语句 p["age"]=22; print(p);
可以用is
关键词来判断类型
var str='1234567'; if(str is String){//判断str是否为字符串类型 print('字符串'); } else if(str is int){ print('数值'); } else{ print('unknown'); }
运算符
//赋值运算符 int b; int a = 10; a??=20;//a不为空,a值仍为10 b??=20;//b为空,将20赋值给b a+=10;//a=a+10 //switch case var sex='男'; switch(sex){ case "男": print("男人") break; case "女": print("女人") break; default: print("未知性别") break; } //三目运算符 int grade = 70; grade>=60?print("及格"):print("不及格"); //??运算符 int a = 20; int b = a??10; //a为空,将10赋值给b //a不为空,将a赋值给b
类型转换
Number与String之间类型转换
String转换为int类型用int.prase()
String转换为double类型用double.prase()
转换的字符串为空时,使用int.prase()、double.prase()
均会报错
String str1 = '123';//整数类型 var myNum1 = int.parse(str1); String str2 = '123.1';//浮点类型 var myNum2 = double.parse(str2); String str3 = '';//为空 //try catch 常常用于捕获异常 try{ var myNum3 = double.parse(str3); }catch(err){ print("出错了"); }
int转换为String类型用toString()
int myNum = 10; var myStr = toString(myNum);
其他类型转换成booleans类型
String str = ''; if(str.isEmpty){...};//用了isEmpty就变成bool类型 int myNum = 0; if(myNum == 0){...};//用了关系运算符变成bool类型
循环语句
自增自减运算
int i=0; i++; i--; //++--在前面,先运算后赋值 //++--在后面,先赋值后运算 int a = 10; int b = a++;//b=10,a=11 int c = ++a;//c=a=12;
for循环
for(int i=0;i<10;i++){ print(i); }//打印0-9
while循环&do while循环
-
while与dowhile的差别在于第一次循环条件不成立的情况
-
用while循环需要避免死循环
-
continue和break:
continue:跳出当前循环进行下一次循环
break:跳出循环
int i = 0; while(i<10){ i++; } do{ i++; }while(i<10); //break while(true){ i++; if(i=10){ break; } }
print输出
-
print(整型or字符型...)
-
print(数据结构的名):直接输出该数据结构内容
-
print("...... $变量名"):在一句话中加上并输出变量的值
void main() { int Num = 5; int price = 50; print('单价为$price'); print('数量为$Num'); print('总价格为:'); print(Num * price); }
Dart集合类型
List
dart中集合和数组都用list来表示
List可以通过索引来取值的
List l1 = [];//初始化一个空的集合 List l2 = [1,2,3,6]; List l3 = <String>['水果','米饭','蔬菜'];//指定数组内容为什么类型
List常用属性
-
length:长度
-
reversed:翻转,只是翻转显示出来,并没有实质性的翻转数组,所以这个是属性
-
isEmpty :是否为空
-
isNotEmpty:是否不为空
List l1 = []; l1.add(3); print(l1.length);//length后不需要加() //翻转(属性) List l1 = <int>[1, 2, 3, 4, 5]; print(l1.reversed);//打印54321 print(l1);//仍为12345 //为空 if(l1.isEmpty){ print('数组为空'); }else{ print('数组不为空'); }
List常用方法
-
add:在数组加一个数据
-
addAll:一次加多个数据
List l1 = <int>[1, 2, 3, 4, 5]; l1.add(6); l1.addAll([7,8,9,10]);
-
indexOf:打印索引下标值,找不到返回-1
List l1 = <int>[1, 2, 3, 4, 5]; print(l1.indexOf(3));//输出为2 print(l1.indexOf(10));//输出为-1
-
remove(具体值):根据值删除某一个数据
-
removeA(索引值):根据索引值删除某一个数据
-
fillRange:修改
List l1 = <int>[1, 2, 3, 4, 5]; l1.fillrange(1,2,10);//左闭右开的区间,区间内改为10
-
insert(index,value):指定位置插入
-
insertAll(index,List):指定位置擦插入另一个数组
-
toList:其他类型转换为List
-
join:List转换为字符串
List l1 = <int>[1, 2, 3, 4, 5]; String str = l1.join(',');//每个数据以逗号分割,但是整体是一个字符串
-
split:字符串转换为List
String str = '1-2-3-4-5'; List l1 = str.split('-');//对字符串中以-分割成一个数组
Set
Set最主要的功能就是去除数组重复内容
Set是没有顺序且不能重复的集合,不可以通过索引去获取值
List myList = ['香蕉','土豆','西瓜','香蕉','西瓜']; Set s = new Set(); s.addAll(myList);//实现对这个数组的去重
Map
Map映射 '...':'...'
Map m = new Map(); m['name'] = '李四';//如果m中没有name,这句则会为m添加一个值为李四的name
Map常用属性
-
keys:获取所有键值
-
values:获取所有的值
-
isEmpty:为空
-
isNotEmpty:不为空
Map常用方法
-
addAll:添加多个数据
Map person ={ 'name':'张三', 'age':20, 'gender':'男' }; person.addAll({'no':'2104239999','work':['敲代码','送外卖']}); print(person);
-
remove(键值)
-
containValue(实值):是否包含这个数据,返回true或false
常用循环方法
forEach、map、where、any、every
//item in遍历 List l1 = <int>[1, 2, 3, 4, 5]; for(var item in l1){ print(item); } //forEach遍历 l1.forEach((value){//参数value是遍历时List的值传入的 print(value);//输出这个value }); //map方法遍历 var newList1 = l1.map((value){ return value*2;//将遍历获取的值*2返回到newList1中 }); //where方法遍历 var newList2 = l1.where((value){ return value>5;//将遍历获取的大于5的值返回到newList2中 }); //any方法遍历 var f1 = l1.any((value){ return value>5;//返回的是true和false,只要集合有一个满足条件大于5就返回true }); //every方法遍历 var f2 = l1.every((value){ return value>5;//返回的是true和false,集合中每一个都要满足条件大于5就返回true,否则返回false });
函数
函数定义
返回类型 方法名称(参数1,参数2...){ 方法体; return 返回值; }
在 dart 里,被省略的函数返回值不是 void,dart 可以允许你不写函数的返回值,编译器会自动帮助你返回 null
//定义一个函数 int myAdd(val1,val2){ return val1+val2; } //作用域 void test01(){ test02(){//在函数内部再定义一个函数 print('t02'); myAdd(1,2);//test02内部可以调用最外层的myAdd函数,myAdd是全局 } test02();//只能在test01的内部调用这个函数 } //定义一个方法,获得一个数k,求1到一个数的和 int getSum(int k){ int ans = 0; for(int i=0;i<=k;i++){ ans+=i; } return ans; }
可选参数
可选参数在参数列表中是带一个[]
创建变量时,如果想要一个变量可为null,需要显式声明为可空的类型,具体就是使用?
进行声明。比如String?、int?等,表示变量是一个可空类型,可以赋值为空
String myPrint(String name,[int? age]){ if(age!=NULL){ return '姓名:$name 年龄:$age'; }else{ return '姓名:$name 年龄保密'; } }
默认参数
创建变量时,可以给变量赋上一个初值
String myPrint(String name,[int age=0]){ if(age!=0){ return '姓名:$name 年龄:$age'; }else{ return '姓名:$name 年龄保密'; } }
命名参数
命名参数在参数列表中是带一个{}
,命名参数在调用函数传参时要用参数名:参数值
的格式
String myPrint(String name,{int age=0}){ if(age!=0){ return '姓名:$name 年龄:$age'; }else{ return '姓名:$name 年龄保密'; } } print(myPrint('dirk',age:20)); print(myPrint('dirk'));
实现一个把方法当作参数的方法:
void fn1(){ print('fn1'); } void fn2(fn){ fn(); } //调用 fn2(fn1);
箭头函数
=>
右边只能跟一句话
//两种方式 var newList=list.map((value)=>value>2?value*2:value);//一句 var newList=list.map((value)=>{ value>2?value*2:value//一句 });
匿名函数
var 变量名=(){...};
将一个方法赋值给一个变量,这个方法是匿名的
匿名函数最后有;
结尾,而普通函数不需要
var myPrint = (String str){ print(str); print(str); print(str); }; myPrint('hahaha');
自执行方法
程序一执行自执行方法就会执行,不需要特别调用
((int n){ print('自执行方法'); print(n); })();
闭包
闭包 即一个函数对象,即使函数对象的调用在它原始作用域之外,依然能够访问在它词法作用域内的变量。
函数嵌套函数且返回里面的函数
-
全局变量:常驻内存、污染全局
-
局部变量:不常驻内存,会被垃圾机制回收,不会污染全局
-
闭包的功能:常驻内存,且不污染全局
fn(){ int a = 123; return(){ a++; print(a); }; } var b = fn(); b(); b(); b();
我们的 a 是在 fn
内部定义的,可是我们可以通过返回的 Function 在外部访问到了这个变量。而我们的 b
则一直保存了 num
。
类、对象
面向对象编程OOP的三个基本特征:封装、继承、多态
封装
定义一个类与实例化这个类
class Person { String name = '张三'; int age = 23; void getInfo() { print('${this.name} ${this.age}'); } } void main() { //实例化用new Person p1 = new Person(); p1.getInfo(); }
构造函数
-
默认构造函数
默认构造函数只能写一个
实例化时给出各成员变量的值需要提前定义好构造函数,编译器不会提供(与C++不一样)
class Person { String name = '张三'; int age = 23; //默认构造函数 Person(String name, int age) { this.name = name; this.age = age; } } void main() { //实例化用new Person p1 = new Person('dirk',24);//调用默认构造函数,没有定义构造函数,这句会报错 }
默认构造函数简写
class Person { String name = '张三'; int age = 23; //默认构造函数 Person(this.name, this.age); }
-
命名构造函数
命名构造函数可以写多个
class Person { String name = '张三'; int age = 23; //默认构造函数 Person(this.name, this.age); //命名构造函数 Person.now(){ print('命名构造函数'); } }
-
把类单独抽离成一个文件
主程序文件头加上
import 'lib/Person.dart';
-
私有属性或方法
dart语言中没有public、protected、private这些访问修饰符号,但是我们可以使用
_
把一个属性或者方法定义为私有//Person类文件 class Person{ String _name;//私有 int age; Person(String name,int age){ this._name = name; this.age = age; } String getName(){ print(this._name); } } //主程序文件 import 'lib/Person.dart'; void main(){ Person p1 = new Person('dirk',24); print(p1._name);//error 无法访问 print(p1.age); print(p1.getName());//通过类内的方法来访问私有属性 }
静态成员
-
静态成员就是使用关键字static来实现类级别的变量和函数
-
静态方法不能访问非静态成员,非静态方法和静态方法均可以访问静态成员
-
静态成员可以直接通过类名访问,不需要实例化
class Person { static String name = 'dirk'; int age = 24; //静态方法不可以访问非静态成员age static void show() { print(name); } void printInfo() { print(name); print(age); } } main() { print(Person.name); //不需要通过实例化访问,可以直接通过类名访问 Person.show(); Person p1 = new Person(); p1.printInfo(); }
对象操作符
-
?
条件运算符p?.printIfo();//p如果为空,不会打印;p非空,会打印数据
-
as
类型转换 -
is
类型判断if(p is Person){ p.name = '李四'; }
-
..
级联操作person p1 = new Person('dirk',24); //赋值和打印 连起来 p1..name = 'melo' ..age = 20 ..printInfo();
继承
子类使用extends关键词来继承父类
子类会继承父类里面可见的属性和方法,但是不会继承构造函数
子类能复写父类的方法getter、setter
//定义父类 class Person{ String name = '张三'; int age = 20; Person(this.name,this.age); } //定义子类 class Student extends Person{ //因为Person有构造函数,需要提供Person构建时的参数,super将子类构造的参数传到Person类中 Student(String name,num age) : super(name,age){} } main(){ Student stu = new Student('dirk',24); print(stu.name); print(stu.age); }
-
子类覆写父类的方法
//定义父类 class Person{ String name = '张三'; int age = 20; Person(this.name,this.age); void printInfo(){ print('${this.name} ${this.age}'); } } //定义子类 class Student extends Person{ //因为Person有构造函数,需要提供Person构建时的参数,super将子类构造的参数传到Person类中 Student(String name,num age) : super(name,age){} @override //建议在覆写前加上这句 void printInfo(){ print('覆写成功!'); } } main(){ Student stu = new Student('melo', 20); stu.printInfo();//输出的是 覆写成功! }
抽象类
-
Dart抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口
-
抽象类通过abstract关键字来定义
-
Dart中的抽象方法不能用abstract声明,Dart中没有方法体的方法我们称为抽象方法
-
子类继承抽象类必须得实现里面的抽象方法
-
如果把抽象类当作接口实现的话必须得实现抽象类里面的定义的所有属性和方法
-
抽象类不能被实例化,只有继承它的子类可以
-
extends和implements区别:
如果要复用抽象类里面的方法,并且要用抽象方法约束子类的话用extends
如果只是把抽象类当作标准的话我们就用implements(接口)实现抽象类
//抽象类不能实例化 abstract class Animal{ eat();//抽象方法 } class Dog extends Animal{ @override eat(){ print('小狗吃骨头'); } } class Cat extends Animal{ @override eat(){ print('小猫吃🐟'); } } main(){ Dog d = new Dog(); d.eat(); }
多态
-
允许将子类类型指针赋值给父类型的指针,同一个函数调用会有不同的执行效果
-
子类的实例赋值给父类的引用
-
多态就是父类定义的一个方法不去实现,让继承它的子类去实现,每个子类表现不同
abstract class Animal{ eat();//抽象方法 } class Dog extends Animal{ @override eat(){ print('小狗吃骨头'); } } main(){ Animal d = new Dog();//子类实例赋值给父类的引用 d.eat(); }
接口
-
dart的接口没有interface关键字定义接口,而是普通类或抽象类都可以作为接口被实现
-
抽象类可以定义抽象方法,普通类不可以,所以一般如果要实现Java接口那样的方式,一般使用抽象类
abstract class Db { String? uri; add(); save(); delete(); } //Db是一个接口 class Mysql implements Db { @override String? uri; Mysql(this.uri); @override add() { // TODO: implement add print('这是MySql的add方法'); } @override delete() { // TODO: implement delete print('这是MySql的delete方法'); } @override save() { // TODO: implement save print('这是MySql的save方法'); } } main() { Mysql m = new Mysql('root'); m.add(); m.delete(); m.save(); }
一个类实现多个接口
abstract class A{ String? name; printA(); } abstract class B{ printB(); } //一个类C实现多个接口 class C implements A,B{ @override String? name; @override printA(){ print('A'); } @override printB(){ print('B'); } }
Mixins
-
作为mixins的类只能继承自Object,不能继承其他类
-
作为mixins的类不能有构造函数
-
一个类可以mixins多个mixins类
-
mixins绝不是继承,也不是接口,而是一种全新特性
class A{ String info = 'this is A'; void printA(){ print("A"); } } class B{ void printB(){ print('B'); } } class C with A,B{ }
泛型
解决类、接口、方法的复用性,以及对不特定数据类型的支持(类型校验)
泛型方法
T getData<t>(T value){ return value; } main(){ print(getData<String>('你好')); }
泛型类
class Mylist<T> { List list = <T>[]; void add(T value){ this.list.add(value); } List getList(){ return list; } }
泛型接口
abstract class Cache<T>{ getByKey(String key); void setByKey(String key,T value); } //文件缓存 class FileCache<T> implements Cache<T>{ @override getByKey(String key){ return null; } @override void setByKey(String key, T value){ print('我是文件缓存${key} ${value}'); } } //内存缓存 class MemoryCache<T> implements Cache<T>{ @override getByKey(String key){ return null; } @override void setByKey(String key, T value){ print('我是内存缓存${key} ${value}'); } } void main(){ MemoryCache m1 = new MemoryCache<String>(); m1.setByKey('index','12345'); MemoryCache m2 = new MemoryCache<Map>(); m2.setByKey('index',{'name':'张三'}); }
库
自定义库
import 'lib/xxx.dart';
系统内置库
import 'dart:math';
import 'dart:io';
async是让方法变成异步
await是等待异步方法执行完成,只有async方法才能使用await关键字调用方法
import 'dart:convert';
Pub包管理系统中的库
https://pub.dev/packages
下载本地后import 'package:http/http.dart' as http;
在项目中引入库
import 'dart:convert' as convert; import 'package:http/http.dart' as http; void main(List<String> arguments) async { // This example uses the Google Books API to search for books about http. // https://developers.google.com/books/docs/overview var url = Uri.https('www.googleapis.com', '/books/v1/volumes', {'q': '{http}'}); // Await the http get response, then decode the json-formatted response. var response = await http.get(url); if (response.statusCode == 200) { var jsonResponse = convert.jsonDecode(response.body) as Map<String, dynamic>; var itemCount = jsonResponse['totalItems']; print('Number of books about http: $itemCount.'); } else { print('Request failed with status: ${response.statusCode}.'); } }
Dart2.1.3之后的一些新特性
Null safety
帮助开发者避免一些日常开发中很难被发现的错误,并且额外的好处是可以改善性能
-
可空类型
?
int a =123; a = null;//error a非空 int? b = 123;//?表示为可空类型 b = null;//可以 String? getData(apiUrl){ if(apiUrl!=null){ return 'this is server data'; } return null;//返回一个null,所以要返回类型可为空 }
-
类型断言
!
String? str = 'this is str'; str = null; print(str!.length); //类型断言:如果str非空则打印str长度,如果str等于null,则会抛出一个异常 void printLength(String? str){ print(str!.length); }
late 关键字
用于延迟初始化
class Person{ late String name;//未给初值会报错,这里使用了late延迟初始化就不会报错 late int age; Person(this.name,this.age); } main(){ Person p1 = new Person('dirk',24); }
required关键字
最开始 @required是注解 现在已经作为内置修饰符,主要用于允许根据需要标记任何命名参数(函数或类),使它们不为空,因为可选参数中必须有一个required
//命名参数中如果没有初值,则需要加上required关键字,表示这是必须传入的参数 String printUserInfo(String username,{required int age ,required String sex}){ return '姓名:$username 性别:$sex 年龄:$age'; }
Dart性能优化之常量、常量构造函数详解
Dart常量
-
final修饰符:final声明的常量允许声明后再赋值,赋值后不可改变,final声明变量是运行时确定的,惰性初始化
-
const修饰符:const声明的常量是编译时确定的,永远不会改变
main(){ const PI = 3.14; final a; a = 13;//先声明后赋值 //a = 14; 报错,只能赋值一次 print(a); }
core库中identical函数
通过identical函数可以检查两个引用是否指向同一个对象
返回一个bool类型的值,true-共用存储空间,false-不共用存储空间
void main(){ var o1 = new Object(); var o2 = new Object(); identical(o1,o2);//返回false var p1 = const Object(); var p2 = const Object(); identical(p1,p2);//返回true }
const关键词在多个地方创建相同对象的时候,内存中只保留了一个对象
常量构造函数
-
常量构造函数需以const关键字修饰
-
const构造函数必须用于成员变量都是final的类
-
如果实例化不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实体
-
实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象
-
Flutter中const修饰不仅仅是节省组件构建时的内存开销,Flutter在需要重新构建组件是不应该改变的,重新构建没有任何意义,因此Flutter不会重新构建const组件
class Container{ final int width; final int height; const Container({required this.width,required this.height}); } main(){ var c1 = Container(width:100,height:100); var c2 = Container(width:100,height:100); identical(c1,c2);//false var c3 = Container(width:100,height:100); var c4 = Container(width:100,height:100); identical(c3,c4);//true }