int
和 double
是num
的子类;
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 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();
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
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();
map[1] = 'android';
//map["name"] = 'flutter'; Error
print(map);
var autoSet = Set();
autoSet.add('dongnao');
autoSet.add(1);
autoSet.add(1);
print('dynamicSet :${autoSet}');//{dongnao, 1}
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))
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;
}
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();
}
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
Dart也提供了异常处理机制,主要有两种异常类型Exception和Error,当然我们也可以继承这两种基础异常类型,然后自定义新的异常类型。
// 直接抛出String对象
throw '欢迎访问www.tizi365.com';
//抛出异常对象的例子:
throw Exception('参数错误!');
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
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函数的调用。
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;
}
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
}
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 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(); // 可以调用 抽象类 中的一般资源
}
// 抽象类不能直接被实列化
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 unknownType;
Dog (this.unknownType);
T show() {
print(this.unknownType);
return this.unknownType;
}
}
void main() {
var d = Dog("woman");
d.show();
var d2 = Dog(123);
d2.show();
}
// 泛型函数
T show(T arr) {
return arr;
}
void main() {
var res = show(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 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);
}
默认情况调用包中的函数或者类,不需要包名作为前缀,上面调用了math包中的max函数,直接使用包中的函数名。但是这样会存在命名冲突的可能性,如果导入的两个包,包含了同名的类或者函数,就会出现命名冲突,因此提供别名机制。
// 使用 as 关键词,指定包的别名
import 'dart:math' as math;
void main() {
// 使用别名,引用包中的函数 。
var a = math.max(1,100);
print(a);
}
有时候我们不想导入整个包,只想导入包里面的某个类或者某个函数。
// 仅导入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支持异步编程模型
// 导入第三方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.");
}
输出结果:
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.");
}
输出结果:
使用格式:
await for (数据类型 变量 in stream类型变量) {
// 处理数据
}
使用await标记for in循环语句,循环读取stream类型的变量中的数据,代码书写也很直观,跟同步代码的书写方式一致
Isolate
实现,Isolate你可以简单的理解是一种特殊的线程。通过异步机制,可以同时处理多个接口请求之类的异步任务,不是也有类似并发的效果吗?那么为什么还需要Isolate并发机制。
Isolate的特点:
不共享内存,意味着你不能像线程那样通过变量共享状态,每个Isolate都有自己独立的内存,这样设计的好处就是你不用加锁,也能安全的操作自己的数据。
// 导入isolate包
import 'dart:isolate';
void main() {
// 通过Isolate.spawn静态函数,创建一个新的Isolate
Isolate.spawn(aaa, "Task1 parameter");
// main函数结束标记
print("main func end.");
}
void aaa(String msg) {
print("aaa recv: $msg");
}
输出结果:
isolate可以使用 async + await
关键词等待Isolate执行结束:
// 导入isolate包
import 'dart:isolate';
void main async() {
// 通过Isolate.spawn静态函数,创建一个新的Isolate
await Isolate.spawn(aaa, "Task1 parameter");
// main函数结束标记
print("main func end.");
}
void aaa(String msg) {
print("aaa recv: $msg");
}
输出结果:
多个Isolate 之间只能通过消息通讯。
例如,我们如何获取一个Isolate的计算结果,主要通过ReceivePort和SendPort两个类处理消息通讯。
ReceivePort
负责接收 SendPort
发送的消息,
SendPort 和 ReceivePort 是捆绑关系, SendPort 是由 ReceivePort 创建的。
// 导入isolate包
import 'dart:isolate';
void main() async {
// 创建一个ReceivePort用于接收消息
var recv = ReceivePort();//------------
Isolate.spawn(subTask, recv.sendPort);
// 使用await等待recv的 第一条 消息
var result = await recv.first;//------------
print("recv: $result");
}
// Isolate入口函数定义,接收一个SendPort对象作为参数
void subTask(SendPort port) {
// 使用SendPort发送一条字符串消息
port.send("subTask Result."); //------------
}