数据类型
浮点类型 double
String //大写
int a = 10;
// 同c# $符号意思一样 不同的是要放在字符串内部
String s1 = "可以用${a}";
//字符串前加r 代表这是一个原始字符串
String s1 = r"可以用${a}";
dart 中所有类型都是对象 所有对象的默认值都是null
如果变量的类型是动态的变化的,可以用dynamic声明
dynamic d = "temp";
print(d is String); // true
d = 1;
print(d is int); // true
常量可以用final和const的来声明
final和const都可以做类型的推断
与var的声明方式一样
也可以具体的赋予某个类型
但是不能和var混用。
final和const的区别
- final的变量只初始化一次,可以是编译时的常量,也可以不是。const必须是编译时的常量。
- final只是在第一次运行的时候确定常量值,const必须指定常量值。比如:final可以是一个函数的计算结果,但是const不行。
- const可以修饰值,final不可以。const的值必须是const, final可以不是。
ps: 编译时常量,是指在程序编译时就可以确定的值。比如:一个确定的数值,或者可以获得结果的数值表达式;一个确定的字符串等等。
List
列表
很简单
具体方法可以查阅文档 https://api.dartlang.org/stable/2.0.0/dart-core/List-class.html
Set
集合
不能指定长度 没有index
Map
键值对
运算符
算术运算符
算数运算符有:+(加)、-(减)、*(乘)、/(除)、~/(除,但结果返回整数)、-expr(负号)以及%(取余)。
关系运算符
关系运算符有:==(等于)、!=(不等于)、>(大于)、<(小于)、>=(大于等于)和<=(小于等于)。
类型测试运算符
类型测试运算符有:is(判断是否某一类)、is!(判断是否不是某一类)和as(将对象作为某一类运行)。
位运算符
位运算符有:&(按位与)、|(按位或)、^(按位异或)、~(按位取反)、<<(按位左移)和>>(按位右移)。
逻辑运算符
逻辑运算符有:&&(与)、||(或)、!(非)。
其他的运算符
?? (是否为空)、expr? val1:val2(三元运算符)、…(级联运算符)、.(成员访问)、?.(有条件的成员访问)、()(函数调用)、
var nullVar;
var intVar = 12;
// 如果 nullVar是空,则赋值intVar
var nullVar;
var intVar = 12;
// 如果 nullVar是空,则赋值intVar
nullVar ??= intVar;
print(nullVar); // 12
// 如果intVar大于10则返回a否则返回b
var strVar = intVar > 10 ? "a": "b";
print(strVar); // a
// … 允许你在同一个对象上连续进行操作
var listVar = [1, 2, 3, 4];
listVar.toSet() // 返回一个set对象
..add(5) // 继续操作上一个set对象
..add(1); // 继续操作上一个set对象
// 相当于
var setVar = listVar.toSet();
setVar.add(5);
setVar.add(1);
// ?. 允许左侧的对象为空
var mapVar;
print(mapVar?.length); // null
print(mapVar.length); // 报错,mapVar是空
print(setVar?.length);
条件判断和循环基本和c#一样 需要注意asset
ssert语句在产品环境下是没有作用的,只有在开发状态下才会有效果。
flutter需要开启Debug模式; dart或者dartjs命令,需要添加参数–enable-asserts
// assert_test.dart文件中的代码
var intVar = 30;
assert(intVar > 10);
print("int ${intVar}");
assert(intVar < 20);
print("int ${intVar}");
// dart assert_test.dart的结果
// int 30
// int 30
/
// dart --enable-asserts assert_test.dart 的结果
// int 30
// Failed assertion: line 50 pos 10: 'intVar < 20': is not true.
函数
dart 函数也是一个对象
函数都是基于类Function
函数也可以作为一个参数 传入其他函数中
所有函数都有返回值 没有指定返回值 则返回null
// 定义一个不带返回值的函数,可以带void, 也可以不带
void say(var word){
print("I say '${word}'";
}
// 参数可以不用指定类型,如果不指定,则默认是var
bool isInt(numVar){
return numVar is int;
}
// 如果函数只有一个return 语句可以用 => 简写
// 需要注意 => 后面只能是一个表达式或者是单条语句。
bool isInt2(numVar) => numVar is int;
main(){
say('hello world'); // I say 'hello world'
print(isInt('a')); // false
print(isInt(1)); // true
}
可选参数
定义可选参数有两种方式一种使用{}, 另一种使用[]。
区别就是, 如果用{}声明,在使用时必须带着参数名,如:a:123;
如果用[]声明,在使用时需要按照顺序使用
void travel({String from, String to}){
print("From ${from} to ${to}");
}
void travel2(String from, [String to, String city]){
print("From ${from} to ${to} and ${city}");
}
main(){
travel(); // From null to null
// 必须传入的是哪个参数的值
travel(to:"usa"); // From null to usa;
travel(from:"china", to:"usa") // From china to usa;
travel2("china"); // From china to null and null;
// 按照顺序传入参数值
travel2("china", "usa"); // From china to usa and null;
travel2("china", "usa", "england"); // From china to usa and england;
}
可选的参数 也可以使用@required 指定可选参数变成必填参数
要使用@required需要引入包 package:meta/meta.dart,如果是用flutter,则可以引入包package:flutter/material.dart。
void travel({@required String from , String to}){}
参数默认值
ps: 老代码中可能用:来赋予参数默认值 不过将在后续版本中去掉 最好韩式用 =
void travel({String from="china", String to="usa"}){
print("From ${from} to ${to}");
}
// list和map的默认值需要用const修饰
void animals([List<String> names= const ["cat", "dog"], Map<String, String> nameMap = const{
"c": "cat",
"d": "dog"
}]){
print("animals: ${names}");
print("nameMap: ${nameMap}");
}
main(){
travel(); // From china to usa;
travel(from:"japan"); // From japan to usa;
animals(); // animals:['cat', 'dog'] nameMap: {'c', 'cat', 'd': 'dog'}
}
匿名参数
定义:
([[Type] param1[, …]]) {
codeBlock;
};
main(List<String> args) {
var listVar = ["cat", 'dog'];
listVar.forEach((animal){
print("animal is ${animal}");
});
// 如果只有一行代码,可以使用=>
listVar.forEach((animal) => print("animal is ${animal}"));
}
作用域
var level1 = "level1";
main(List<String> args) {
var level2 = "level2";
// 定义一个内部函数
void testScope(){
var level3 = "level3";
print(level1); // level1
print(level2); // level2
print(level3); // level3
}
// 运行内部函数
testScope();
}
闭包
闭包是一个函数对象,可以读取其他作用域内的变量使用。
// 本实例来自于官网
Function makeAdder(num){
return (addNum){
return addNum + num;
};
}
main(){
var add5 = makeAdder(5);
var add10 = makeAdder(10);
print(add5(1)); // 6
print(add10(1)); // 11
}
main 函数
main函数是整个应用的入口 每个应用必须有一个main函数作为入口
main函数没有返回值 并且有一个list类型的变量
捕获异常
使用on可以捕获到某一类的异常,但是获取不到异常对象; catch可以捕获到异常对象。这个两个关键字可以组合使用。
try{
testException();
} on FormatException catch(e){ // 如果匹配不到FormatException,则会继续匹配
print("catch format exception");
print(e);
rethrow; // 重新抛出异常
} on Exception{ // 匹配不到Exception,会继续匹配
print("catch exception") ;
}catch(e, r){ // 匹配所以类型的异常. e是异常对象,r是StackTrace对象,异常的堆栈信息
print(e);
}
finally
finally 是内部语句 无论是否有异常 都会执行
testException(){
throw FormatException("this is exception");
}
main(List<String> args) {
try{
testException();
} on FormatException catch(e){
print("catch format exception");
print(e);
rethrow;
} on Exception{
print("catch exception") ;
}catch(e, r){
print(e);
}finally{
print("this is finally"); // 在rethrow之前执行
}
}
类
每一个对象都有一个runtimeType的属性,通过这个属性可以获取到该对象的类型(Type的对象)。
main(){
// 返回的是一个Type的对象
print(Animal().runtimeType); // Animal
}
class Animal(){String name;}
实例变量
如果一个变量类型不是final类型的 那么实例变量都会有隐式的Get Set方法 不然就只有Get方法
如果一个实例变量有初始值 那么他的值在构造方法生效前 就已经被赋予
如果想重写Get和Set方法,可以用关键字get和set实现
以_开头的变量和方法 都是私有的
// 官网实例代码
class Rectangle {
num left = 10;
num _left_width = 12; // 私有变量
// 定义多个变量
num top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// 定义两个计算属性: right and bottom.
// 需要注意的是,get和set需要成对出现
// 重写get
num get right => left + width;
// 重写set
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
void main() {
var rect = Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
构造函数
造函数的定义,可以通过函数名为ClassName和ClassName.identifier(命名的构造函数 Named Constructors)定义。
通过命名的构造函数可以轻松实现多个不同的构造函数。
如果类没有构造函数,那么默认会有一个没有参数的构造函数。
class Animal{
String name;
String color = "yellow";
Animal(String name){
this.name = name;
}
// 上面的构造函数,可以简写为
Animal(this.name);
Animal.createInstance(Map values){
name = values["name"];
}
run(){
print("${this.name} is running");
}
}
main(List<String> args) {
var animal = Animal.createInstance({"name": "dog"});
print(animal.name);
}
子类构造函数
父类中的命名构造函数不能被子类继承。如果想要子类也拥有一个父类一样名字的构造函数,必须在子类是实现这个构造函数。默认情况下,子类只能调用父类的无名,无参数的构造函数。父类的无名构造函数会在子类的构造函数前调用。如果initializer list 也同时定义了,则会先执行initializer list 中的内容,然后在执行父类的无名无参数构造函数,最后调用子类自己的无名无参数构造函数。即下面的顺序:
initializer list(初始化列表)
superclass’s no-arg constructor
main class’s no-arg constructor
如果父类不显示提供无名无参数构造函数的构造函数,在子类中必须显式调用父类的一个构造函数。这种情况下,调用父类的构造函数的代码放在子类构造函数名后,子类构造函数体前,中间使用:(colon) 分割。
在开发状态下,初始化列表可以使用assert表达式。
class Animal{
String name;
String color = "yellow";
Animal.createInstance(Map values){
print("in animal");
name = values["name"];
}
run(){
print("${this.name} is running");
}
}
// extends 继承的关键字
class Bird extends Animal{
String name = "blue bird";
String desc;
Bird.createInstance(Map values):
desc = name, // 初始化列表,注意这里没有this
super.createInstance(values){ // 父类方法调用使用super
print("in bird");
}
}
main(List<String> args) {
var bird = Bird.createInstance({
"name": "bird"
});
print(bird.name); // bird
print(bird.desc); // blue bird
}
重定向构造函数
所谓重定向构造函数,是指在一个构造函数中指向另一个构造函数,但是重定向构造函数的函数体是空的。以官网代码为例。
class Point {
num x, y;
// 主构造函数
Point(this.x, this.y);
// 重定向构造函数
Point.alongXAxis(num x) : this(x, 0);
}
静态构造函数
如果类的对象不会有任何变化,那么可以让这些对象是编译时常量。通过创建静态构造函数,并且所有成员属性是final的来实现。
静态构造函数,并不是都会返回一个编译时常量。
class Animal{
final String name;
final String color;
// 静态构造函数
const Animal(this.name, this.color);
}
main(List<String> args) {
var cat = const Animal("cat", "yellow"); // 常量
var cat2 = const Animal("cat", "yellow");
var cat3 = Animal("cat", "yellow"); // 非常量
// 构造两个相同的编译时常量,实际上是一个常量
print(identical(cat, cat2)); // true
print(identical(cat, cat3)); // false
// 在常量上下文中,可以省略构造函数或文字前面的常量
const cats = const {
'bluscat': const [const Animal("cat", "blue")],
'yellowcat': const [const Aminal("cat", "yellow")],
};
// 上面的代码可以写成下面这样,这是Dart2的新特性
const cats2 = {
'bluscat': [Animal("cat", "blue")],
'yellowcat': [Aminal("cat", "yellow")],
};
}
工厂构造函数
使用关键字final实现工厂构造函数。工厂构造函数可以让你不必要每次都创建一个新的对象,就好像是有一个缓存,缓存了老的对象。
工厂构造函数中不能使用this。
下面是官网的实例:
class Logger {
final String name;
bool mute = false;
// 私有变量
static final Map<String, Logger> _cache =
<String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = new Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) {
print(msg);
}
}
}
void main() {
var p1 = Logger("Part1");
p1.log("this is part1");
var p2 = Logger("Part2");
p2.log("this is part2");
}
抽象类和方法
使用关键字abstract声明的类,就是抽象类。抽象方法是没有实现体的方法,抽象方法只能存在于抽象类中。
实例方法、Setter和Getter方法都可以是抽象方法。
抽象类不能被实例化。如果想要抽象类进行实例化,可以用工厂构造函数实现。
抽象类通常用来定义接口。
abstract class Animal{
String color = "blue";
// 抽象方法
run();
}
class Bird extends Animal{
String name;
Bird(this.name);
// 抽象方法的实现
run(){
print("A ${this.color} ${this.name} is flying");
}
}
main(List<String> args) {
var bird = Bird("tom");
bird.run();
}
隐式接口
Dart2中的每一个类都有一个隐式的接口,这个隐式的接口包含所有的实例成员和实现的接口的实例成员。
如果你想创建一个类A支持类B的API函数,但是不想继承B的实现,则类A需要继承类B的接口。
继承接口的关键字是implements, 一个类可以继承多个接口。
class Animal{
String _name; // 在接口中,但是是私有的,所以只能在本类中使用
Animal(this._name); // 构造函数不在接口中
run(){
print("${this._name} is running");
}
}
// 实现Animal的接口,如果还有其他的接口,可以用,分割 implements Animal, ...
class Bird implements Animal{
String _name;
Bird(this._name);
run(){
print("${this._name} can not run");
}
}
runningAnimal(Animal animal){
animal.run();
}
main(List<String> args) {
runningAnimal(Animal("dog")); // dog is running
runningAnimal(Bird("bird")); // bird can not run
}
类的继承
使用extends来继承父类,super来指定父类。
子类可以重载父类的实例方法、Setter和Getter,使用@override注释重新的方法。
重载noSuchMethod() 方法,可以在对象视图调用不存在的方法或者变量时调用。但是,你不能调用一个不存在的方法,除非是以下情况的一种:
-
一个静态类型的dynamic的变量;
-
接收方有一个定义未实现的方法的静态类型(抽象的也可以),并且接收方是dynamic并且有一个noSuchMethod()的实现,与类Object中的实现不同。
class Animal{
String name;
String color = "yellow";
Animal.createInstance(Map values){
print("in animal");
name = values["name"];
}
run(){
print("${this.name} is running");
}
}
// extends 继承的关键字
class Bird extends Animal{
String name = "blue bird";
String desc;
Bird.createInstance(Map values):
desc = name, // 初始化列表,注意这里没有this
super.createInstance(values){ // 父类方法调用使用super
print("in bird");
}
@override
run(){
pirnt("${this.name} can not run");
}
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
枚举
枚举类型是一种特殊的类,通常用来表示相同类型的一组常量。使用关键字enum定义枚举。
枚举的每一个值都有一个index属性,index从0开始计数。
枚举不能被继承,不能创建实例。
enum Animal {
cat,
dog,
bird
}
main(List<String> args) {
print(Animal.dog.index); // 1
// 获取所有枚举值
List<Animal> animals = Animal.values;
Animal dog = Animal.dog;
switch (dog) {
case Animal.cat:
print("animal is cat");
break;
case Animal.dog:
print("animal is dog");
break;
default:
print("which animal?");
}
// animal is dog
}
mixins
mixins是给类添加新的特性的方式,也是一种重用类代码的一种方式。
mixins的关键字是with。详细的使用,可以查看文档。
with不能单独使用,必须跟着extends使用。
class Fly{
fly(){
print("flying");
}
}
class Animal{}
class Bird extends Animal with Fly{
}
main(List<String> args) {
var bird = Bird();
bird.fly(); // flying
}
类变量和方法
使用关键字static,声明类变量和类方法
class Animal{
static var name; // 类变量
static const color = "yellow"; // 类常量
// 类方法
static run(){
print("running");
}
}
main(List<String> args) {
Animal.name = "dog";
print(Animal.name); // dog
Animal.run(); // running
}
库
库的引入
使用import关键字引入库。引入库有如下三种方式:
-
引入同库文件,直接使用库的路径: import “Path/name.dart”;
-
引入Dart语言内部的库,路径前需要添加dart: imprrt “dart:html”;
-
引入第三方库,路径前需要添加package: import “package:Path/name.dart”。
引入的库可以用as来重命名,防止不同的库变量名冲突。当使用了别名,那么在调用引入的库中的对象时,需要带着别名,如:asName.ClassName。
使用show和hide关键字,可以单独引入或者剔除库中的某一个API。
import "hello.dart"; // 引入同目录下的hello.dart文件
import "http://xxx/xx.dart"; // 也可是url,但是不推荐
import "dart:html"; // 引入Dart2中的html库
import "package:lib1/lib1.dart"; // 引入lib1包下的lib1.dart
import "package:lib2/lib2.dart" as lib2; // 引入lib2包下的lib2.dart, 并重命名为lib2
import "package:lib3.dart" show Foo; // 只引入lib3.dart中的Foo对象
import "package:lib4.dart" hide Foo; // 除了Foo对象,引入其他所有lib4.dart中的对象
// 使用Element 来自lib1
Element element1 = Element();
// 使用Element 来自lib2
lib2.Element element2 = lib2.Element();
库的拆分
我可以将一个比较大的库文件拆成几个比较小的文件,这些文件可以通过part关联。每一个part关联的文件,它们之间共享所有的命名空间,同时共享所有的对象,包括私有的对象。
如果使用了part拆分,库必须使用library声明。
PS: part的引入文件,也可以使用url但是不推荐。如: part “http://xxx/xx.dart”;
// 创建文件test.dart
library testall;
import "dart:html";
part("test1.dart"); // 关联test1.dart文件
part("test2.dart");
// 创建test1.dart
part of testall; // 声明是testall的一部分,不必再引入dart:html,可以直接使用
// 创建test2.dart
part of testall; // 声明是testall的一部分
延迟加载
使用deferred as 关键字,可以实现库的延迟加载。使用loadLibrary()进行加载,可以多次调用,但是只会执行一次,它返回一个Future对象。
为什么使用延迟加载?
-
减少APP的启动时间;
-
延迟加载很少使用的功能
import 'package:greetings/hello.dart' deferred as hello; // 延迟加载的声明,默认重命名
Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
异步
Dart库中的大多数函数都返回了Future和Stream对象。这些函数都是异步的,它们在设置一个可能耗时的操作(比如I/O)之后返回,而无需等待操作完成。
实现异步的关键字是: async和await。
异步函数
使用async来声明一个异步函数,异步函数都会返回一个Future对象。
如果没有返回任何内容,可以声明为Future。
异步函数在调用时,会立即返回,不会等程序体执行完毕。
Future lookUpVersion() async => ‘1.0.0’;
Futrue
如果你需要一个Future对象,需要满足下面两点:
-
使用async和await;
-
调用Future的API。
await必须在async声明的函数体中使用,使用方式: await
expression通常是一个Future对象,如果不是,会自动包装成一个Future对象
Future checkVersion() async {
var version = await lookUpVersion();
// Do something with version
}
await可以使用try,catch,finally来处理异常和清理代码
try {
version = await lookUpVersion();
} catch (e) {
// React to inability to look up the version
}
// await可以使用多次
var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
Stream
如果你想从一个Stream中获取值,你需要满足下面两个条件:
-
使用async和一个异步循环(await for);
-
使用Stream的API。
// expression必须是一个Stream对象
await for (varOrType identifier in expression) {
// Executes each time the stream emits a value.
}
异步循环执行流程:
等待Stream发出一个值;
执行 for 循环的主体,把变量设置为发出的值;
重复 1 和 2,直到 Stream 关闭。
在异步循环中,break和continue还是有效的。
转自 白菜大叔
原文:https://blog.csdn.net/shanyuu/article/details/83008881