Dart (火箭式入门)

Dart语言基本介绍

  • 每个语句之后都要以;结尾
  • 支持顶层函数(main函数)
  • 强类型,但是支持类型推断:var name = ‘tom’
  • 如果不想指定具体的类型,可以使用dynamic

变量

数字类型

intdoublenum的子类;

  num a = 123;
  a = 111;

字符串

  String aaa = "name";
  aaa = '123';

长字符串

  String s1 = "123" 'abc' "okok";    //123abcokok
  String s2 = "123" + 'abc';     //123abc
  
  String s3 = '''ewrwerAeawrwer  
    eee''';   //ewrwerAeawrwer 换行 eee
    
  String s4 = '''ewrwerAeawrwer\neee''';    //ewrwerAeawrwer 换行 eee
  String s5 = r'''ewrwerAeawrwer\neee''';   //ewrwerAeawrwer\neee

布尔

  bool bo;
  print(bo);//输出:null
  bo = true;
  print(bo);//输出:true

List

  • 不定类型,不定长度
  List autoList = List();  
   // List autoList = new List()..length = 2; // --null占位--式 初始化
   // var arr = [1,2,3,4]  // 字面量 式
  autoList..add(1)..add(2)..add('damon'); // push元素
  print('autoList: ${autoList}'); //autoList: [1, 2, damon]
  • 元素类型固定
  var typeList = List<int>();
  typeList.add(1);
  //typeList.add("aaa"); Error
  print('typeList: ${typeList}'); //typeList: [1]
  • 固定长度
  List list = List(3); //List的声明,可以用var也可用List。
  list[0] = 1; 
  list[1] = 2;
  list[2] = 'damon';
  // list[3] = 'damon';  Error: RangeError (index)
  print('list: ${list}'); // list: [1, 2, damon]
  • 常用属性
  print(autoList.first); //第一个元素
  print(autoList.last); //最后一个元素
  print(autoList.length); //长度
  • 常用方法
  // 増
  autoList.add(11);
  autoList.addAll([2, 3, 4]);
  autoList.insert(0,0);
  autoList.insertAll(1, [5, 6, 7]);
  print(autoList);//[0, 5, 6, 7, 1, 2, damon, 11, 2, 3, 4]

  // 删
  autoList.remove(5); // 删除值为 5 的元素 
  autoList.removeAt(0);// 删除索引为 0 的元素
   
  // 改
  autoList[0] = "zero";
  // [1,2] + [3,4] = [1,2,3,4]
  
  // 查
  print(autoList.contains("damon")); //true
  print(autoList.indexOf("damon"));//4

Map

  • 不定类型,不定长度
  var autoMap = Map();
  // var map1 = {'name': 'tom', 1: 'value'};也可如此声明
  autoMap['name'] = 'tom';
  autoMap[1] = 'value';
  print(autoMap); //{name: tom, 1: value}
  • 常用属性方法
  print(autoMap.length); //键值对个数
  print(autoMap.keys); //key 集合
  print(autoMap.values); //value集合
  autoMap.addAll({'name1':'damon'});
  • 强类型
  var map = Map<int, String>();
  map[1] = 'android';
  //map["name"] = 'flutter'; Error
  print(map);

Set

  var autoSet = Set();
  autoSet.add('dongnao');
  autoSet.add(1);
  autoSet.add(1);
  print('dynamicSet :${autoSet}');//{dongnao, 1}

类型转换、类型判定

  • as 用于类型转换,将一个对象类型转换成另外一种对象类型,一般用于子类对象转换成父类对象。
  • is 用于检测一个变量是否属于某种对象类型
  • is! 用于检测一个变量不属于某种对象类型
num a = 1;
int i = a as int;
print(iNum is int);  // true

 var aaa = [1,2,3];
 print(aaa is List);  // true

// 如果 emp变量是Person类型则条件为true
if (emp is Person) {
  // 忽略代码
}
// 永远返回true, 因为所有类型都继承了Object。
if (emp is Object) {
    // 忽略代码
}

说明:如果变量是某个类的子类的实例,那么这个变量也属于父类类型,is条件返回true。

变量定义修饰符

  • 变量var、dynamic、Object
    未初始化变量声明
    var、dynamic、Object若未初始化的变量的话,变量值是可以动态改变的
    初始化变量声明
    var声明初始化后不可再改变类型(类似go的:=语法),而dynamic和Object可以
    dynamic 编译阶段不检查类型,而Object会在编译阶段会检查类型;

  • 常量final、const
    有类型推导,所以 声明的类型 可省略:

const String name = "tom";
// 省略
const name = "tom";

const的相关说明:

  • const可使用其他const 常量的值来初始化其值: const a = 1; const sum = a;
  • 相同的const常量不会在内存中重复创建
  • const需要是编译时常量;const dt = DateTime.now();//报错,const必须是编译时常量

函数

所有的函数都有返回值。如果没有指定返回值,则默认把语句 return null; 作为函数的最后一个语句执行;
这是为了和变量保持一致性:所有的变量只声明不初始化,那么该变量的值就是null。

  • 定义
  // 有类型定义
  int add(int a,int b) {
    return a+b;
  }
  //无类型定义(不推荐)
  add2(a, b) {
    return a+b;
  }
  print(add2(1,2))
  • 可选命名参数
    使用 {param1, param2, …} 的形式来指定命名参数。
  int add({int x, int y, int z}) {
    x ??= 1;
    y ??= 2;
    z ??= 3;
    return x + y + z;
  }

  print(add()); // 6
  print(add(x : 0, y : 0)); // 3
  print(add(x : 0, y : 0, z : 0)); // 0
  • 可选位置参数
    把可选参数放到 [] 中,必填参数要放在可选参数前面。
  int add4(int x, [int y, int z]) {
    y ??= 2;
    z ??= 3;
    return x + y + z;
  }
  • 可选命名参数默认值
    (默认值必须是编译时常量),可以使用等号‘=’或冒号’:‘。
    (Dart SDK 1.21 之前只能用冒号,冒号的支持以后会移除,所以建议使用等号)
 int add5(int x, {int y = 2, int z = 3}) {
    return x + y + z;
  }
  
  print(add5(1, y: 10, z: 2));
  • 可选位置参数默认值
    (默认值必须是编译时常量),只能使用等号’=’。
 int add6(int x, [int y = 2, int z = 3]) {
    return x + y + z;
  }

  print(add6(1));

常用操作符

?.

条件成员访问 和 . 类似,但是左边的操作对象不能为 null,
例如 foo?.bar 如果 foo 为 null 则返回 null,否则返回 bar 成员

~/

被除数 ÷ 除数 = 商 … 余数

  print(2 / 3);   	//输出结果:0.6666666666666666
  print(2 ~/ 3);  	//输出结果:0

条件表达式

String res = false ? 'yes' : 'no';
String res = null ?? 'no';

级联操作符

…可以在同一个对象上 连续调用多个函数以及访问成员变量。
严格来说, 两个点的级联语法不是一个操作符。 只是一个 Dart 特殊语法。

class Person {
  String name;
  int age;
  Person(this.name, this.age);

  void printInfo() {
    print('name is ${this.name}, age is ${this.age}');
  }
}

void main() {

  var obj = new Person('sada', 20);
  obj..name = 'new Name'
     ..age = 22
     ..printInfo();
     
}

流程控制语句

  • if else
  • for, forEach, for-in
  • while , do-whild
  • break,continue
  • switch case
  var collection = [11, 22, 33];
 
  //forEach: item 为元素
  collection.forEach((item) => print('forEach: $item'));
  
  //item 为元素
  for (var item in collection) {
    print('for-in: $item');
  }
  // i 为索引
  for (var i = 0;i<collection.length; i++) {
    print(i);
  }

异常处理

Dart也提供了异常处理机制,主要有两种异常类型Exception和Error,当然我们也可以继承这两种基础异常类型,然后自定义新的异常类型。

抛出异常(throw)

// 直接抛出String对象
throw '欢迎访问www.tizi365.com';

//抛出异常对象的例子:
throw Exception('参数错误!');

捕获异常(catch)

try {
  // 业务代码

} on OutOfLlamasException { // 通过on 指定要拦截的异常类型
  // 处理异常1

} on Exception catch (e) { // 通过on 指定拦截的异常类型,通过 catch 捕获异常对象
  // 拦截所有Exception和他的子类抛出的异常。
  print('Unknown exception: $e');

} catch (e) { 
  // 没有指定异常类型,只是catch异常对象,那相当于捕获所有异常类型。
  print('Something really unknown: $e');

}

最终执行(finally)

如果我们希望无论是否捕获到异常,都要执行一些操作。例如,关闭文件。可以使用finally

try {
  // 业务代码
} catch (e) { 
  print('Error: $e'); // 处理异常
} finally { // 无论是否捕获异常都会执行finally的代码
  cleanTask(); // 执行清理工作。
}

默认构造函数只能有一个,命名构造函数可以有多个。

普通构造函数

class Person {
  // 类属性声明
  // 未初始化实例变量的默认人值为 “null”
  String name;
  int age;
  int sex;
  
  // 默认构造函数(同 类名 重名 的函数)
  Person(String name, int age, [int sex = 1]) {
    this.name = name;
    this.age = age;
    this.sex = sex;
  }
  // 简写形式:
  // Person(this.name, this.age, this.sex);

  // 方法声明
  void getInfo() {
    print(
        'My name is ${name}, my age is ${age}, and I am a ${sex == 1 ? 'man' : 'woman'}');
  }

  void changeName(String name) {
      this.name = name;
  }
}

void main() {
  Person p = new Person('ada', 20, 0);
  p.getInfo();
  
  var obj = Person('ada', 20, 0);
  obj.getInfo();
}

命名构造函数

使用命名构造函数可以为一个类实现多个构造函数, 更清晰的表明你的意图。

class Aaa {
  num x;
  num y;
  //默认构造函数
  Aaa(this.x, this.y);
  
  //命名构造函数 tom
  Aaa.tom(a,b) {
    this.x = a;
    this.y = b;
  }

  //命名构造函数 cat
  Aaa.cat(a,b) {
    this.x = a;
    this.y = b;
  }
}

void main() {
  
  var Tom = new Aaa.tom(111,222);
  var Cat = new Aaa.cat(333,444);
  print(Tom.x);
  print(Cat.x);
  
}

属性代理

在dart中属性都不是直接访问的,所有对字段属性的引用都是对属性访问器函数的调用, 只有访问器函数才能直接访问它的状态。
而在未显示设置 set和 get时 给实例属性赋值或获取值时,实际上内部都是对setter和getter函数的调用。

setter
class Rectangle {
  num left, top, width, height;
  Rectangle(this.left, this.top, this.width, this.height);
  
  set bottom(num value) {
    print("赋予bottom的值是 $num");
    top = value - height;
  }
  set right(num value) => left = value - width;
}

main() {
  var rect = Rectangle(3, 4, 20, 15);
  rect.right = 15;
  rect.bottom = 12;
}
getter
class Rectangle {
  num left, top, width, height;
  Rectangle(this.left, this.top, this.width, this.height);
  
  get square => width * height;
}

main() {
  var rect = Rectangle(3, 4, 20, 15);
  print(rect.square);
}

setter,getter函数实现的目的,普通函数也能做到的。
如果类中需要向外暴露类中某个状态那么更适合使用setter,getter函数;
如果是触发类中的某个行为操作,那么普通函数更适合一点。

私有属性、私有方法

Dart没有 public private protected这些访问修饰符合,
但是我们可以使用下划线开头的形式_把一个属性或者方法定义成 私有成分。

  • 私有属性 在继承的子类中是不能使用的
  • 私有方法 在继承的子类中可以使用,但是跨模块(文件)是访问不到的。

静态成员

静态成员不会被继承;
静态方法 —只能访问—> 静态属性
非静态方法 —可以访问—> 静态成员 + 非静态成员

class Person {
  static String name;
  int age;
  
  Person(this.age);
  
  static void setName(String name) {
    Person.name = name;
    // this.age = 20; // Error:静态方法中没有this
  }
  void say() {
    print(Person.name); //静态属性 的 调用方式
    print(this.age); // 非静态属性 的 调用方式
  }
}

void main() {
  Person p = new Person(20);
  // 静态属性测试
  print(p.age);
  // print(p.name); // Error: 'name' isn't defined
  
  // 静态方法测试
  p.say();// 20, null
  Person.setName("tom");
  p.say();// 20, tom
}

类之继承

  • 子类使用extends关键词来继承父类
  • 子类会继承父类里面可见的属性和方法,但是不会继承构造函数
  • 子类能复写父类的方法 getter和setter
class Person {
  String name;
  num age;
  Person(this.name, this.age);
  void printInfo() {
    print('${this.name}----${this.age}');
  }
}

class Web extends Person {
  // 通过super关键字, 通信父类
  Web(String name, num age) : super(name, age);
}

void main() {
  Web w = new Web('hhh', 12);
  // 子类可以 直接调用 父类的 一般属性和方法;
  print(w.name);
  w.printInfo(); 
}

子类的 资源扩展 以及 方法覆盖:

class Person {
  String name;
  num age;
  Person(this.name, this.age);
  void printInfo() {
    print('${this.name}----${this.age}');
  }
}

class Web extends Person {
  String sex; // 子类资源
  
  Web(String name, num age, String sex) : super(name, age) {
    this.sex = sex;
  }
  void run() {
    // 调用父类的方法
    super.printInfo(); 
    // 调用自身的方法
    this.printInfo(); 
    // 调用父类的属性
    print("${this.name}---${this.age}---${this.age}");
  }

  // 覆写父类的方法
  @override // 这个复写标识,可以写 可以不写,建议写
  void printInfo() {
    print('姓名:${this.name}----年龄${this.age}');
  }
}

void main() {
  Web w = new Web('hhh', 12, '女');
  w.printInfo();// 自身优先
  w.run();
}

类之多态

  • 抽象类通过 abstract 关键字来定义
  • 抽象方法无需用abstract声明,只要没有 方法体 的方法 就是 抽象方法。
  • 子类 继承 抽象类 必须得实现里面的抽象方法
  • 把抽象类当做接口实现 的话必须得实现抽象类里面定义的所有属性方法
  • 抽象类不能被实例化,只有继承它的子类可以

extends 抽象类 和 implements 抽象类 的区别:

  • extends:可以直接使用抽象类中的 一般成员
abstract class Animal{ 
  eat();   //抽象方法 
  
  printInfo(){ // 一般方法
    print('我是一个抽象类里面的普通方法');
  }
}

// 定义子类
class Dog extends Animal{
  @override
  eat() { // 父类定义的抽象方法 子类必须实现 否则报错
     print('小狗在吃骨头');
  }

  run() {
    // TODO: implement run
    print('小狗在跑');
  }  
}

main(){
  Dog d=new Dog();
  d.eat(); // 小狗在吃骨头
  d.printInfo(); // 可以调用 抽象类 中的一般资源
}
  • implements:只是讲 抽象类 作为接口来使用。
// 抽象类不能直接被实列化
abstract class Animal{ 
  int aaa;
  void eat();   //抽象方法 
  
  void printInfo(){ // 一般方法
    print('我是一个抽象类里面的普通方法');
  }
}

// 定义子类
class Dog implements Animal{
  int aaa; // 抽象类定的一般属性也要实现
  Dog(this.aaa);
  
  @override
  void eat() { // 抽象类定义的抽象方法 子类必须实现 否则报错
     print('小狗在吃骨头');
  }
  
  void printInfo(){ // 抽象类定义的一般方法 子类也必须实现 否则报错
    print('我是子类中的方法');
  }

  void run() {
    print('小狗在跑');
  }  
}

void main(){
  Dog d=new Dog(110);
  print(d.aaa);
  d.eat(); // 小狗在吃骨头
  d.printInfo(); // 我是子类中的方法
}

混入

Dart语言的类是单继承的(只能继承一个父类),
如果我们要想实现类似多继承的效果,可以使用mixin机制,又叫混入机制,
例如把 类A 混入到 类B 中,那么 类B 就拥有了 类A 的成员,跟继承的特性非常相似。

// 定义一个可以被mixin的类,使用mixin代替class关键词
// 注意:mixin 不能定义构造方法
// mixin其实就是一种 代码复用 机制。
mixin Cat {
  String name = "tom";
  int age = 11;

  void showName() {
    print(this.name);
    print(age);
  }
}

// 使用 with关键词 可以混入多个mixin类,类之间使用逗号分隔。
class Dog with Cat {
  String sex;
  Dog (this.sex);
}

void main() {
  var d = Dog("woman");
  // 调用混入的方法
  d.showName();
  print(d.sex);
}

扩展:继承 + 混入

Man 继承了Person,同时混入了A, B, C三个类
class Man extends Person with A, B, C {
}

泛型

使用 泛型参数 替代代码中 需要变化数据类型
泛型参数使用<>符号,包裹起来,多个泛型参数使用逗号分隔。
一般习惯使用 大写字母代 表泛型参数。

// 泛型类
class Dog <T> {
  T unknownType;
  Dog (this.unknownType);
  T show() {
    print(this.unknownType);
    return this.unknownType;
  }
}
void main() {
  var d = Dog<String>("woman");
  d.show();
  var d2 = Dog<int>(123);
  d2.show();
}

// 泛型函数
T show<T>(T arr) {
  return arr;
}
void main() {
  var res = show<int>(123);
  print(res);
}

泛型约束

有了泛型之后,一个函数或容器类能处理的类型一下子扩到了无限大,似乎有点失控的感觉。所以这里又产生了一个约束的概念。我们可以声明对类型参数进行约束。

class Man {
  String name;
  Man(this.name);
}

class Dog extends Man{
  Dog(String name): super(name);
}

class Cat {
  String name;
  Cat(this.name);
}

void showName<T extends Man>(T obj) {
  print (obj.name);
} 

void main() {
  var m = new Man("man");
  showName(m);
  
  var d = new Dog("dog");
  showName(d);// 子类 依旧符合 Man类型 的要求
  
  var c = new Cat("cat");
  showName(c);// 报错,Cat 不符合 Man类型 的要求
}

包导入

导入内置包

Dart语言内置了一些常用的包,这些内置包随着Dart sdk一起安装在本地。导入内置包使用 dart: 作为路径前缀。

import 'dart:math';

void main() {
  // 调用 包中 的函数或者类,不需要包名作为 前缀
  var a = max(1,100);
  print(a);
}

别名机制 (as)

默认情况调用包中的函数或者类,不需要包名作为前缀,上面调用了math包中的max函数,直接使用包中的函数名。但是这样会存在命名冲突的可能性,如果导入的两个包,包含了同名的类或者函数,就会出现命名冲突,因此提供别名机制。

// 使用 as 关键词,指定包的别名
import 'dart:math' as math;

void main() {
  // 使用别名,引用包中的函数 。
  var a = math.max(1,100);
  print(a);
}

导入包的部分内容 (show,hide)

有时候我们不想导入整个包,只想导入包里面的某个类或者某个函数。

// 仅导入max,sin函数, 
import 'dart:math' show max,sin;

// 除了max,sin函数,导入math中的所有内容。
import 'dart:math' hide max,sin;

导入本地模块

本地模块:libs/aaa.dart
在main.dart文件中导入:

// 直接通过本地文件路径导入dart脚本
import 'libs/aaa.dart';

异步编程

Dart是 单线程 模型的语言,
Dart原生通过Future、async和await支持异步编程模型

Future (类似于js的promise对象)

// 导入第三方http库
import 'package:http/http.dart' as http;

main() {
  var url = "https://www.tizi365.com/";

  var fTask = http.get(url);

  print(fTask);

  // 向future对象注册回调函数,处理请求结果
  fTask.then((response) {
	  print('Response status: ${response.statusCode}');
  });

  // 打印main函数结束标记
  print("main func end.");
}

输出结果:

  • Instance of ‘Future’
  • main func end.
  • Response status: 200

异步函数,同步化 (async, await)

import 'package:http/http.dart' as http;

// 使用 async关键词,标记main函数是一个异步函数。
main() async {
  var url = "https://www.tizi365.com/";

  // 通过await,等待future异步计算任务的结果
  var response = await http.get(url);

  // 打印http请求状态码
  print('Response status: ${response.statusCode}');

  print("main func end.");
}

输出结果:

  • Response status: 200
  • main func end.

Stream (类似于js的迭代器)

使用格式:

await for (数据类型 变量 in stream类型变量) {
  // 处理数据
}

使用await标记for in循环语句,循环读取stream类型的变量中的数据,代码书写也很直观,跟同步代码的书写方式一致

并发编程

  • 在Dart 语言中并发机制由Isolate实现,Isolate你可以简单的理解是一种特殊的线程。
  • 为什么需要多个Isolate实现并发处理?
  • 默认情况Dart启动一个Isolate,main函数就是他的入口,这就是为什么说Dart是单线程,默认只是启动一个Isolate, 相当于程序是单线程执行的。
  • 通过异步机制,可以同时处理多个接口请求之类的异步任务,不是也有类似并发的效果吗?那么为什么还需要Isolate并发机制。
  • 首先,一个Isolate不能利用多个CPU(核)
  • 再则,异步机制 的 并发 只适合 处理 网络IO密集行,遇到 计算密集 就歇菜了

Isolate的特点:

  • Isolate之间不共享内存。
  • Isolate之间只能通过消息通讯。

不共享内存,意味着你不能像线程那样通过变量共享状态,每个Isolate都有自己独立的内存,这样设计的好处就是你不用加锁,也能安全的操作自己的数据。

// 导入isolate包
import 'dart:isolate';

void main() {
        // 通过Isolate.spawn静态函数,创建一个新的Isolate
	Isolate.spawn<String>(aaa, "Task1 parameter");
        
  // main函数结束标记 
	print("main func end.");
}

void aaa(String msg) {
	print("aaa recv: $msg");
}

输出结果:

  • main func end.
  • aaa recv: Task1 parameter

isolate可以使用 async + await 关键词等待Isolate执行结束:

// 导入isolate包
import 'dart:isolate';

void main async() {
        // 通过Isolate.spawn静态函数,创建一个新的Isolate
	await Isolate.spawn<String>(aaa, "Task1 parameter");
        
  // main函数结束标记 
	print("main func end.");
}

void aaa(String msg) {
	print("aaa recv: $msg");
}

输出结果:

  • aaa recv: Task1 parameter
  • main func end.

Isolate 消息通讯

  • 多个Isolate 之间只能通过消息通讯。

  • 例如,我们如何获取一个Isolate的计算结果,主要通过ReceivePort和SendPort两个类处理消息通讯。

  • ReceivePort 负责接收 SendPort 发送的消息,

  • SendPort 和 ReceivePort 是捆绑关系, SendPort 是由 ReceivePort 创建的。

// 导入isolate包
import 'dart:isolate';

void main() async {
  // 创建一个ReceivePort用于接收消息
  var recv = ReceivePort();//------------

	Isolate.spawn<SendPort>(subTask, recv.sendPort);
        
  // 使用await等待recv的 第一条 消息
  var result = await recv.first;//------------

  print("recv: $result");
}

// Isolate入口函数定义,接收一个SendPort对象作为参数
void subTask(SendPort port) {

  // 使用SendPort发送一条字符串消息
	port.send("subTask Result."); //------------

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值